activerecord-sqlanywhere-adapter 0.1.3 → 1.0.0
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.
- data/CHANGELOG +6 -0
- data/LICENSE +1 -1
- data/README +1 -71
- data/lib/active_record/connection_adapters/sqlanywhere_adapter.rb +101 -75
- data/lib/arel/visitors/sqlanywhere.rb +107 -0
- metadata +42 -22
- data/test/sqlanywhere.drop.sql +0 -35
- data/test/sqlanywhere.sql +0 -222
- data/test/sqlanywhere2.drop.sql +0 -4
- data/test/sqlanywhere2.sql +0 -5
data/CHANGELOG
CHANGED
@@ -1,5 +1,11 @@
|
|
1
1
|
=CHANGE LOG
|
2
2
|
|
3
|
+
=====0.2.0 -- 2010/12/02
|
4
|
+
- Added support for Rails 3.0.3
|
5
|
+
- Added support for Arel 2
|
6
|
+
- Removed test instructions for ActiveRecord 2.2.2
|
7
|
+
- Updated license to 2010
|
8
|
+
|
3
9
|
=====0.1.3 -- 2010/02/01
|
4
10
|
- Added :encoding option to connection string
|
5
11
|
- Fixed bug associated with dangling connections in development mode (http://groups.google.com/group/sql-anywhere-web-development/browse_thread/thread/79fa81bdfcf84c13/e29074e5b8b7ad6a?lnk=gst&q=activerecord#e29074e5b8b7ad6a)
|
data/LICENSE
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
/*====================================================
|
2
2
|
*
|
3
|
-
* Copyright 2008-
|
3
|
+
* Copyright 2008-2010 iAnywhere Solutions, Inc.
|
4
4
|
*
|
5
5
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
6
6
|
* you may not use this file except in compliance with the License.
|
data/README
CHANGED
@@ -5,7 +5,7 @@ native SQL Anywhere Ruby driver. To get the native driver, use:
|
|
5
5
|
|
6
6
|
gem install sqlanywhere
|
7
7
|
|
8
|
-
This driver is designed for use with ActiveRecord
|
8
|
+
This driver is designed for use with ActiveRecord 3.0.3 and greater.
|
9
9
|
|
10
10
|
This driver is licensed under the Apache License, Version 2.
|
11
11
|
|
@@ -41,14 +41,6 @@ The following code is a sample database configuration object.
|
|
41
41
|
|
42
42
|
3. Copy <tt>test/connection.rb</tt> into the newly created directory.
|
43
43
|
|
44
|
-
NOTE: If using ActiveRecord 2.0.2, change the second line of the file from:
|
45
|
-
|
46
|
-
require_dependency 'models/course'
|
47
|
-
|
48
|
-
to:
|
49
|
-
|
50
|
-
require_dependency 'fixtures/course'
|
51
|
-
|
52
44
|
4. Create the two test databases. These can be created in any directory.
|
53
45
|
|
54
46
|
dbinit -c arunit
|
@@ -57,70 +49,8 @@ The following code is a sample database configuration object.
|
|
57
49
|
|
58
50
|
<b>If the commands cannot be found, make sure you have set up the SQL Anywhere environment variables correctly.</b> For more information review the online documentation here[http://dcx.sybase.com/index.php#http%3A%2F%2Fdcx.sybase.com%2F1100en%2Fdbadmin_en11%2Fda-envvar-sect1-3672410.html].
|
59
51
|
|
60
|
-
5. If you are using ActiveRecord 2.0.2, you must load the test tables.
|
61
|
-
|
62
|
-
dbisql -c "eng=arunit;dbn=arunit;uid=dba;pwd=sql" sqlanywhere.sql
|
63
|
-
dbisql -c "eng=arunit;dbn=arunit2;uid=dba;pwd=sql" sqlanywhere2.sql
|
64
|
-
|
65
|
-
If you are using a newer version of ActiveRecord, this schema is automatically
|
66
|
-
migrated for you.
|
67
|
-
|
68
52
|
6. Run the unit test suite from the ActiveRecord install directory:
|
69
53
|
|
70
54
|
rake test_sqlanywhere
|
71
55
|
|
72
56
|
<b>If the migration tests fail, make sure you have set up the SQL Anywhere environment variables correctly.</b> For more information review the online documentation here[http://dcx.sybase.com/index.php#http%3A%2F%2Fdcx.sybase.com%2F1100en%2Fdbadmin_en11%2Fda-envvar-sect1-3672410.html].
|
73
|
-
|
74
|
-
==Explaination of Test Results
|
75
|
-
|
76
|
-
As of ActiveRecord 2.2.2., it is expected that 6 tests will fail. The failed tests, along with an explaination, are identified below:
|
77
|
-
|
78
|
-
1. (CalculationsTest) - test_should_sum_expression
|
79
|
-
|
80
|
-
Explaination: Appears to be an error in the test.
|
81
|
-
|
82
|
-
This tests checks the results of the following statement:
|
83
|
-
|
84
|
-
assert_equal '636', Account.sum("2 * credit_limit")
|
85
|
-
|
86
|
-
According to the ActiveRecord documentation, the summation of a column should return a value of the same type as the column. In this case, <tt>credit_limit</tt> is an integer, and so the result should be a number type, not a string.
|
87
|
-
|
88
|
-
2. (MigrationTest) - test_add_table_with_decimals
|
89
|
-
|
90
|
-
Explaination: Requires special case.
|
91
|
-
|
92
|
-
From the comments regarding this test:
|
93
|
-
|
94
|
-
# This one is fun. The 'value_of_e' field is defined as 'DECIMAL' with
|
95
|
-
# precision/scale explicitly left out. By the SQL standard, numbers
|
96
|
-
# assigned to this field should be truncated but that's seldom respected.
|
97
|
-
|
98
|
-
There are already three special cases of this test. SQL Anywhere would require another special case to pass this test.
|
99
|
-
|
100
|
-
3. (NamedScopeTest) - test_should_use_where_in_query_for_named_scope
|
101
|
-
|
102
|
-
Explaination: Appears to be an error in the test.
|
103
|
-
|
104
|
-
This test issues a query that returns two rows. Because there is no ORDER BY in the query, the order that that rows are returned in is non-deterministic. SQL Anywhere returns the correct rows, but in a different order.
|
105
|
-
|
106
|
-
4. (QueryCacheTest) - test_cache_does_not_wrap_string_results_in_arrays
|
107
|
-
|
108
|
-
Explaination: Appears to be an error in the test.
|
109
|
-
|
110
|
-
This tests checks that the cached value of the following query is a string:
|
111
|
-
|
112
|
-
SELECT count(*) AS count_all FROM tasks
|
113
|
-
|
114
|
-
However, SQL Anywhere treats the values of a <tt>COUNT(*)</tt> opration as a fixnum, not a string. It would appear that the real intent of this test is to test that the value is NOT an array, rather than testing that the value is a string.
|
115
|
-
|
116
|
-
5. (SchemaDumperTest) - test_schema_dump_includes_limit_constraint_for_integer_columns
|
117
|
-
|
118
|
-
Explaination: SQL Anywhere does not have a 'limitless' integer type.
|
119
|
-
|
120
|
-
Any integer type will be given an implicit limit when instantiated. It would be possible to use another type such as <tt>NUMERIC</tt> to mimic this type, however this seems like a bad idea.
|
121
|
-
|
122
|
-
6. (ValidationsTest) - test_validate_case_sensitive_uniqueness
|
123
|
-
|
124
|
-
Explaination: By default, SQL Anywhere is case insensitive.
|
125
|
-
|
126
|
-
If a case sensitive database is required (as in this test), ensure the database is created with the <tt>-c</tt> switch to make it case sensitive.
|
@@ -23,6 +23,7 @@
|
|
23
23
|
#====================================================
|
24
24
|
|
25
25
|
require 'active_record/connection_adapters/abstract_adapter'
|
26
|
+
require 'arel/visitors/sqlanywhere.rb'
|
26
27
|
|
27
28
|
# Singleton class to hold a valid instance of the SQLAnywhereInterface across all connections
|
28
29
|
class SA
|
@@ -30,7 +31,7 @@ class SA
|
|
30
31
|
attr_accessor :api
|
31
32
|
|
32
33
|
def initialize
|
33
|
-
|
34
|
+
require 'sqlanywhere' unless defined? SQLAnywhere
|
34
35
|
@api = SQLAnywhere::SQLAnywhereInterface.new()
|
35
36
|
raise LoadError, "Could not load SQLAnywhere DBCAPI library" if SQLAnywhere::API.sqlany_initialize_interface(@api) == 0
|
36
37
|
raise LoadError, "Could not initialize SQLAnywhere DBCAPI library" if @api.sqlany_init() == 0
|
@@ -69,6 +70,17 @@ module ActiveRecord
|
|
69
70
|
end
|
70
71
|
|
71
72
|
module ConnectionAdapters
|
73
|
+
class SQLAnywhereException < StandardError
|
74
|
+
attr_reader :errno
|
75
|
+
attr_reader :sql
|
76
|
+
|
77
|
+
def initialize(message, errno, sql)
|
78
|
+
super(message)
|
79
|
+
@errno = errno
|
80
|
+
@sql = sql
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
72
84
|
class SQLAnywhereColumn < Column
|
73
85
|
private
|
74
86
|
# Overridden to handle SQL Anywhere integer, varchar, binary, and timestamp types
|
@@ -83,10 +95,14 @@ module ActiveRecord
|
|
83
95
|
|
84
96
|
def extract_limit(sql_type)
|
85
97
|
case sql_type
|
86
|
-
when /^tinyint/i
|
87
|
-
|
88
|
-
when /^
|
89
|
-
|
98
|
+
when /^tinyint/i
|
99
|
+
1
|
100
|
+
when /^smallint/i
|
101
|
+
2
|
102
|
+
when /^integer/i
|
103
|
+
4
|
104
|
+
when /^bigint/i
|
105
|
+
8
|
90
106
|
else super
|
91
107
|
end
|
92
108
|
end
|
@@ -105,7 +121,7 @@ module ActiveRecord
|
|
105
121
|
end
|
106
122
|
|
107
123
|
class SQLAnywhereAdapter < AbstractAdapter
|
108
|
-
def initialize( connection, logger
|
124
|
+
def initialize( connection, logger, connection_string = "") #:nodoc:
|
109
125
|
super(connection, logger)
|
110
126
|
@auto_commit = true
|
111
127
|
@affected_rows = 0
|
@@ -187,7 +203,7 @@ module ActiveRecord
|
|
187
203
|
when String, ActiveSupport::Multibyte::Chars
|
188
204
|
value_S = value.to_s
|
189
205
|
if column && column.type == :binary && column.class.respond_to?(:string_to_binary)
|
190
|
-
"
|
206
|
+
"'#{column.class.string_to_binary(value_S)}'"
|
191
207
|
else
|
192
208
|
super(value, column)
|
193
209
|
end
|
@@ -205,76 +221,53 @@ module ActiveRecord
|
|
205
221
|
end
|
206
222
|
|
207
223
|
|
208
|
-
#
|
209
|
-
#
|
210
|
-
|
211
|
-
#
|
212
|
-
# This function (distinct) is based on the Oracle ActiveRecord driver created by Graham Jenkins (2005)
|
213
|
-
# (http://svn.rubyonrails.org/rails/adapters/oracle/lib/active_record/connection_adapters/oracle_adapter.rb)
|
214
|
-
def distinct(columns, order_by)
|
224
|
+
# This function (distinct) is based on the Oracle Enhacned ActiveRecord driver maintained by Raimonds Simanovskis (2010)
|
225
|
+
# (https://github.com/rsim/oracle-enhanced)
|
226
|
+
def distinct(columns, order_by) #:nodoc:
|
215
227
|
return "DISTINCT #{columns}" if order_by.blank?
|
216
|
-
|
228
|
+
|
229
|
+
# construct a valid DISTINCT clause, ie. one that includes the ORDER BY columns, using
|
230
|
+
# FIRST_VALUE such that the inclusion of these columns doesn't invalidate the DISTINCT
|
231
|
+
order_columns = if order_by.is_a?(String)
|
232
|
+
order_by.split(',').map { |s| s.strip }.reject(&:blank?)
|
233
|
+
else # in latest ActiveRecord versions order_by is already Array
|
234
|
+
order_by
|
235
|
+
end
|
217
236
|
order_columns = order_columns.zip((0...order_columns.size).to_a).map do |c, i|
|
218
|
-
|
237
|
+
# remove any ASC/DESC modifiers
|
238
|
+
value = c =~ /^(.+)\s+(ASC|DESC)\s*$/i ? $1 : c
|
239
|
+
"FIRST_VALUE(#{value}) OVER (PARTITION BY #{columns} ORDER BY #{c}) AS alias_#{i}__"
|
219
240
|
end
|
220
241
|
sql = "DISTINCT #{columns}, "
|
221
242
|
sql << order_columns * ", "
|
222
|
-
end
|
223
|
-
|
224
|
-
# This function (add_order_by_for_association_limiting) is based on the Oracle ActiveRecord driver created by Graham Jenkins (2005)
|
225
|
-
# (http://svn.rubyonrails.org/rails/adapters/oracle/lib/active_record/connection_adapters/oracle_adapter.rb)
|
226
|
-
def add_order_by_for_association_limiting!(sql, options)
|
227
|
-
return sql if options[:order].blank?
|
228
|
-
|
229
|
-
order = options[:order].split(',').collect { |s| s.strip }.reject(&:blank?)
|
230
|
-
order.map! {|s| $1 if s =~ / (.*)/}
|
231
|
-
order = order.zip((0...order.size).to_a).map { |s,i| "alias_#{i}__ #{s}" }.join(', ')
|
232
|
-
|
233
|
-
sql << " ORDER BY #{order}"
|
234
|
-
end
|
243
|
+
end
|
235
244
|
|
236
245
|
# The database execution function
|
237
246
|
def execute(sql, name = nil) #:nodoc:
|
238
|
-
|
239
|
-
|
247
|
+
if name == :skip_logging
|
248
|
+
query(sql)
|
249
|
+
else
|
250
|
+
log(sql, name) { query(sql) }
|
251
|
+
end
|
252
|
+
end
|
240
253
|
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
# Executes the query, iterates through the results, and builds an array of hashes.
|
245
|
-
rs = SA.instance.api.sqlany_execute_direct(@connection, sql)
|
246
|
-
if rs.nil?
|
247
|
-
error = SA.instance.api.sqlany_error(@connection)
|
248
|
-
case error[0].to_i
|
254
|
+
def translate_exception(exception, message)
|
255
|
+
case exception.errno
|
249
256
|
when -143
|
250
|
-
if sql
|
251
|
-
|
257
|
+
if exception.sql !~ /^SELECT/i then
|
258
|
+
raise ActiveRecord::ActiveRecordError.new(message)
|
252
259
|
else
|
253
|
-
|
260
|
+
super
|
254
261
|
end
|
262
|
+
when -194
|
263
|
+
raise InvalidForeignKey.new(message, exception)
|
264
|
+
when -196
|
265
|
+
raise RecordNotUnique.new(message, exception)
|
266
|
+
when -183
|
267
|
+
raise ArgumentError, message
|
255
268
|
else
|
256
|
-
|
257
|
-
end
|
269
|
+
super
|
258
270
|
end
|
259
|
-
|
260
|
-
record = []
|
261
|
-
if( SA.instance.api.sqlany_num_cols(rs) > 0 )
|
262
|
-
while SA.instance.api.sqlany_fetch_next(rs) == 1
|
263
|
-
max_cols = SA.instance.api.sqlany_num_cols(rs)
|
264
|
-
result = Hash.new()
|
265
|
-
max_cols.times do |cols|
|
266
|
-
result[SA.instance.api.sqlany_get_column_info(rs, cols)[2]] = SA.instance.api.sqlany_get_column(rs, cols)[1]
|
267
|
-
end
|
268
|
-
record << result
|
269
|
-
end
|
270
|
-
@affected_rows = 0
|
271
|
-
else
|
272
|
-
@affected_rows = SA.instance.api.sqlany_affected_rows(rs)
|
273
|
-
end
|
274
|
-
SA.instance.api.sqlany_free_stmt(rs)
|
275
|
-
|
276
|
-
SA.instance.api.sqlany_commit(@connection) if @auto_commit
|
277
|
-
return record
|
278
271
|
end
|
279
272
|
|
280
273
|
# The database update function.
|
@@ -356,10 +349,12 @@ module ActiveRecord
|
|
356
349
|
column_type_sql = 'bigint'
|
357
350
|
else
|
358
351
|
column_type_sql = 'integer'
|
359
|
-
|
352
|
+
end
|
360
353
|
column_type_sql
|
361
|
-
|
362
|
-
|
354
|
+
elsif type == :string and !limit.nil?
|
355
|
+
"varchar (#{limit} character)"
|
356
|
+
else
|
357
|
+
super(type, limit, precision, scale)
|
363
358
|
end
|
364
359
|
else
|
365
360
|
super(type, limit, precision, scale)
|
@@ -401,17 +396,13 @@ module ActiveRecord
|
|
401
396
|
end
|
402
397
|
|
403
398
|
def remove_index(table_name, options={}) #:nodoc:
|
404
|
-
execute "DROP INDEX #{table_name}.#{quote_column_name(index_name(table_name, options))}"
|
399
|
+
execute "DROP INDEX #{quote_table_name(table_name)}.#{quote_column_name(index_name(table_name, options))}"
|
405
400
|
end
|
406
401
|
|
407
402
|
def rename_table(name, new_name)
|
408
403
|
execute "ALTER TABLE #{quote_table_name(name)} RENAME #{quote_table_name(new_name)}"
|
409
404
|
end
|
410
405
|
|
411
|
-
def remove_column(table_name, column_name) #:nodoc:
|
412
|
-
execute "ALTER TABLE #{quote_table_name(table_name)} DROP #{quote_column_name(column_name)}"
|
413
|
-
end
|
414
|
-
|
415
406
|
def change_column_default(table_name, column_name, default) #:nodoc:
|
416
407
|
execute "ALTER TABLE #{quote_table_name(table_name)} ALTER #{quote_column_name(column_name)} DEFAULT #{quote(default)}"
|
417
408
|
end
|
@@ -501,7 +492,7 @@ module ActiveRecord
|
|
501
492
|
select_components = modified_sql.scan(/\ASELECT\s+(DISTINCT)?(.*?)(?:\s+LIMIT\s+(.*?))?(?:\s+OFFSET\s+(.*?))?\Z/xmi)
|
502
493
|
return modified_sql if select_components[0].nil?
|
503
494
|
final_sql = "SELECT #{select_components[0][0]} "
|
504
|
-
final_sql << "TOP #{select_components[0][2]
|
495
|
+
final_sql << "TOP #{select_components[0][2].nil? ? 1000000 : select_components[0][2]} "
|
505
496
|
final_sql << "START AT #{(select_components[0][3].to_i + 1).to_s} " unless select_components[0][3].nil?
|
506
497
|
final_sql << "#{select_components[0][1]}"
|
507
498
|
return final_sql
|
@@ -538,9 +529,9 @@ FROM
|
|
538
529
|
WHERE
|
539
530
|
table_name = '#{table_name}'
|
540
531
|
SQL
|
541
|
-
|
542
|
-
|
543
|
-
|
532
|
+
structure = execute(sql, :skip_logging)
|
533
|
+
raise(ActiveRecord::StatementInvalid, "Could not find table '#{table_name}'") if structure == false
|
534
|
+
structure
|
544
535
|
end
|
545
536
|
|
546
537
|
# Required to prevent DEFAULT NULL being added to primary keys
|
@@ -563,9 +554,44 @@ SQL
|
|
563
554
|
def set_connection_options
|
564
555
|
SA.instance.api.sqlany_execute_immediate(@connection, "SET TEMPORARY OPTION non_keywords = 'LOGIN'") rescue nil
|
565
556
|
SA.instance.api.sqlany_execute_immediate(@connection, "SET TEMPORARY OPTION timestamp_format = 'YYYY-MM-DD HH:NN:SS'") rescue nil
|
557
|
+
#SA.instance.api.sqlany_execute_immediate(@connection, "SET OPTION reserved_keywords = 'LIMIT'") rescue nil
|
566
558
|
# The liveness variable is used a low-cost "no-op" to test liveness
|
567
559
|
SA.instance.api.sqlany_execute_immediate(@connection, "CREATE VARIABLE liveness INT") rescue nil
|
568
560
|
end
|
561
|
+
|
562
|
+
def query(sql)
|
563
|
+
return if sql.nil?
|
564
|
+
#sql = modify_limit_offset(sql)
|
565
|
+
|
566
|
+
# ActiveRecord allows a query to return TOP 0. SQL Anywhere requires that the TOP value is a positive integer.
|
567
|
+
return Array.new() if sql =~ /TOP 0/i
|
568
|
+
|
569
|
+
# Executes the query, iterates through the results, and builds an array of hashes.
|
570
|
+
rs = SA.instance.api.sqlany_execute_direct(@connection, sql)
|
571
|
+
if rs.nil?
|
572
|
+
result, errstr = SA.instance.api.sqlany_error(@connection)
|
573
|
+
raise SQLAnywhereException.new(errstr, result, sql)
|
574
|
+
end
|
575
|
+
|
576
|
+
record = []
|
577
|
+
if( SA.instance.api.sqlany_num_cols(rs) > 0 )
|
578
|
+
while SA.instance.api.sqlany_fetch_next(rs) == 1
|
579
|
+
max_cols = SA.instance.api.sqlany_num_cols(rs)
|
580
|
+
result = Hash.new()
|
581
|
+
max_cols.times do |cols|
|
582
|
+
result[SA.instance.api.sqlany_get_column_info(rs, cols)[2]] = SA.instance.api.sqlany_get_column(rs, cols)[1]
|
583
|
+
end
|
584
|
+
record << result
|
585
|
+
end
|
586
|
+
@affected_rows = 0
|
587
|
+
else
|
588
|
+
@affected_rows = SA.instance.api.sqlany_affected_rows(rs)
|
589
|
+
end
|
590
|
+
SA.instance.api.sqlany_free_stmt(rs)
|
591
|
+
|
592
|
+
SA.instance.api.sqlany_commit(@connection) if @auto_commit
|
593
|
+
return record
|
594
|
+
end
|
569
595
|
end
|
570
596
|
end
|
571
597
|
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
module Arel
|
2
|
+
module Visitors
|
3
|
+
class SQLAnywhere < Arel::Visitors::ToSql
|
4
|
+
private
|
5
|
+
def visit_Arel_Nodes_SelectStatement o
|
6
|
+
o = order_hacks(o)
|
7
|
+
|
8
|
+
is_distinct = using_distinct?(o)
|
9
|
+
|
10
|
+
o.limit = 1000000 if (o.offset && !o.limit)
|
11
|
+
|
12
|
+
if (o.limit || o.offset) && is_distinct
|
13
|
+
o.cores.map { |cores| cores.projections.first.gsub!('DISTINCT', '') }
|
14
|
+
#{ |projection| /DISTINCT/ === projection}}
|
15
|
+
end
|
16
|
+
|
17
|
+
[
|
18
|
+
"SELECT",
|
19
|
+
("DISTINCT" if (o.limit || o.offset) && is_distinct),
|
20
|
+
("TOP #{o.limit}" if o.limit),
|
21
|
+
(visit(o.offset) if o.offset),
|
22
|
+
o.cores.map { |x| visit_Arel_Nodes_SelectCore x }.join,
|
23
|
+
("ORDER BY #{o.orders.map { |x| visit x }.join(', ')}" unless o.orders.empty?),
|
24
|
+
#("LIMIT #{o.limit}" if o.limit),
|
25
|
+
#(visit(o.offset) if o.offset),
|
26
|
+
(visit(o.lock) if o.lock),
|
27
|
+
].compact.join ' '
|
28
|
+
end
|
29
|
+
|
30
|
+
def visit_Arel_Nodes_SelectCore o
|
31
|
+
[
|
32
|
+
"#{o.projections.map { |x| visit x }.join ', '}",
|
33
|
+
("FROM #{visit o.froms}" if o.froms),
|
34
|
+
("WHERE #{o.wheres.map { |x| visit x }.join ' AND ' }" unless o.wheres.empty?),
|
35
|
+
("GROUP BY #{o.groups.map { |x| visit x }.join ', ' }" unless o.groups.empty?),
|
36
|
+
(visit(o.having) if o.having),
|
37
|
+
].compact.join ' '
|
38
|
+
end
|
39
|
+
|
40
|
+
|
41
|
+
def visit_Arel_Nodes_Offset o
|
42
|
+
"START AT #{visit(o.expr) + 1}"
|
43
|
+
end
|
44
|
+
|
45
|
+
|
46
|
+
def using_distinct?(o)
|
47
|
+
o.cores.any? do |cores|
|
48
|
+
cores.projections.any? do |projection|
|
49
|
+
/DISTINCT/ === projection
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# The functions (order_hacks, split_order_string) are based on the Oracle Enhacned ActiveRecord driver maintained by Raimonds Simanovskis (2010)
|
55
|
+
# (https://github.com/rsim/oracle-enhanced)
|
56
|
+
|
57
|
+
###
|
58
|
+
# Hacks for the order clauses
|
59
|
+
def order_hacks o
|
60
|
+
return o if o.orders.empty?
|
61
|
+
return o unless o.cores.any? do |core|
|
62
|
+
core.projections.any? do |projection|
|
63
|
+
/DISTINCT.*FIRST_VALUE/ === projection
|
64
|
+
end
|
65
|
+
end
|
66
|
+
# Previous version with join and split broke ORDER BY clause
|
67
|
+
# if it contained functions with several arguments (separated by ',').
|
68
|
+
#
|
69
|
+
# orders = o.orders.map { |x| visit x }.join(', ').split(',')
|
70
|
+
orders = o.orders.map do |x|
|
71
|
+
string = visit x
|
72
|
+
if string.include?(',')
|
73
|
+
split_order_string(string)
|
74
|
+
else
|
75
|
+
string
|
76
|
+
end
|
77
|
+
end.flatten
|
78
|
+
o.orders = []
|
79
|
+
orders.each_with_index do |order, i|
|
80
|
+
o.orders <<
|
81
|
+
Nodes::SqlLiteral.new("alias_#{i}__#{' DESC' if /\bdesc$/i === order}")
|
82
|
+
end
|
83
|
+
o
|
84
|
+
end
|
85
|
+
|
86
|
+
# Split string by commas but count opening and closing brackets
|
87
|
+
# and ignore commas inside brackets.
|
88
|
+
def split_order_string(string)
|
89
|
+
array = []
|
90
|
+
i = 0
|
91
|
+
string.split(',').each do |part|
|
92
|
+
if array[i]
|
93
|
+
array[i] << ',' << part
|
94
|
+
else
|
95
|
+
# to ensure that array[i] will be String and not Arel::Nodes::SqlLiteral
|
96
|
+
array[i] = '' << part
|
97
|
+
end
|
98
|
+
i += 1 if array[i].count('(') == array[i].count(')')
|
99
|
+
end
|
100
|
+
array
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
Arel::Visitors::VISITORS['sqlanywhere'] = Arel::Visitors::SQLAnywhere
|
107
|
+
|
metadata
CHANGED
@@ -1,7 +1,12 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: activerecord-sqlanywhere-adapter
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 1
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
version: 1.0.0
|
5
10
|
platform: ruby
|
6
11
|
authors:
|
7
12
|
- Eric Farrar
|
@@ -9,30 +14,40 @@ autorequire:
|
|
9
14
|
bindir: bin
|
10
15
|
cert_chain: []
|
11
16
|
|
12
|
-
date:
|
17
|
+
date: 2011-02-11 00:00:00 -08:00
|
13
18
|
default_executable:
|
14
19
|
dependencies:
|
15
20
|
- !ruby/object:Gem::Dependency
|
16
21
|
name: sqlanywhere
|
17
|
-
|
18
|
-
|
19
|
-
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
20
25
|
requirements:
|
21
26
|
- - ">="
|
22
27
|
- !ruby/object:Gem::Version
|
23
|
-
|
24
|
-
|
28
|
+
segments:
|
29
|
+
- 0
|
30
|
+
- 1
|
31
|
+
- 5
|
32
|
+
version: 0.1.5
|
33
|
+
type: :runtime
|
34
|
+
version_requirements: *id001
|
25
35
|
- !ruby/object:Gem::Dependency
|
26
36
|
name: activerecord
|
27
|
-
|
28
|
-
|
29
|
-
|
37
|
+
prerelease: false
|
38
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
30
40
|
requirements:
|
31
41
|
- - ">="
|
32
42
|
- !ruby/object:Gem::Version
|
33
|
-
|
34
|
-
|
35
|
-
|
43
|
+
segments:
|
44
|
+
- 3
|
45
|
+
- 0
|
46
|
+
- 3
|
47
|
+
version: 3.0.3
|
48
|
+
type: :runtime
|
49
|
+
version_requirements: *id002
|
50
|
+
description: " ActiveRecord driver for SQL Anywhere\n"
|
36
51
|
email: eric.farrar@ianywhere.com
|
37
52
|
executables: []
|
38
53
|
|
@@ -44,16 +59,15 @@ extra_rdoc_files:
|
|
44
59
|
- LICENSE
|
45
60
|
files:
|
46
61
|
- lib/active_record/connection_adapters/sqlanywhere_adapter.rb
|
47
|
-
-
|
62
|
+
- lib/arel/visitors/sqlanywhere.rb
|
48
63
|
- test/connection.rb
|
49
|
-
- test/sqlanywhere.drop.sql
|
50
|
-
- test/sqlanywhere2.drop.sql
|
51
|
-
- test/sqlanywhere2.sql
|
52
64
|
- README
|
53
65
|
- CHANGELOG
|
54
66
|
- LICENSE
|
55
67
|
has_rdoc: true
|
56
68
|
homepage: http://sqlanywhere.rubyforge.org
|
69
|
+
licenses: []
|
70
|
+
|
57
71
|
post_install_message:
|
58
72
|
rdoc_options:
|
59
73
|
- --title
|
@@ -64,23 +78,29 @@ rdoc_options:
|
|
64
78
|
require_paths:
|
65
79
|
- lib
|
66
80
|
required_ruby_version: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
67
82
|
requirements:
|
68
83
|
- - ">="
|
69
84
|
- !ruby/object:Gem::Version
|
70
|
-
|
71
|
-
|
85
|
+
segments:
|
86
|
+
- 1
|
87
|
+
- 9
|
88
|
+
- 2
|
89
|
+
version: 1.9.2
|
72
90
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
91
|
+
none: false
|
73
92
|
requirements:
|
74
93
|
- - ">="
|
75
94
|
- !ruby/object:Gem::Version
|
95
|
+
segments:
|
96
|
+
- 0
|
76
97
|
version: "0"
|
77
|
-
version:
|
78
98
|
requirements: []
|
79
99
|
|
80
100
|
rubyforge_project: sqlanywhere
|
81
|
-
rubygems_version: 1.3.
|
101
|
+
rubygems_version: 1.3.7
|
82
102
|
signing_key:
|
83
|
-
specification_version:
|
103
|
+
specification_version: 3
|
84
104
|
summary: ActiveRecord driver for SQL Anywhere
|
85
105
|
test_files: []
|
86
106
|
|
data/test/sqlanywhere.drop.sql
DELETED
@@ -1,35 +0,0 @@
|
|
1
|
-
DROP TABLE accounts
|
2
|
-
DROP TABLE funny_jokes
|
3
|
-
DROP TABLE companies
|
4
|
-
DROP TABLE topics
|
5
|
-
DROP TABLE developers
|
6
|
-
DROP TABLE projects
|
7
|
-
DROP TABLE developers_projects
|
8
|
-
DROP TABLE customers
|
9
|
-
DROP TABLE orders
|
10
|
-
DROP TABLE movies
|
11
|
-
DROP TABLE subscribers
|
12
|
-
DROP TABLE booleantests
|
13
|
-
DROP TABLE auto_id_tests
|
14
|
-
DROP TABLE entrants
|
15
|
-
DROP TABLE colnametests
|
16
|
-
DROP TABLE mixins
|
17
|
-
DROP TABLE people
|
18
|
-
DROP TABLE readers
|
19
|
-
DROP TABLE binaries
|
20
|
-
DROP TABLE computers
|
21
|
-
DROP TABLE tasks
|
22
|
-
DROP TABLE posts
|
23
|
-
DROP TABLE comments
|
24
|
-
DROP TABLE authors
|
25
|
-
DROP TABLE categories
|
26
|
-
DROP TABLE categories_posts
|
27
|
-
DROP TABLE fk_test_has_fk
|
28
|
-
DROP TABLE fk_test_has_pk
|
29
|
-
DROP TABLE keyboards
|
30
|
-
DROP TABLE legacy_things
|
31
|
-
DROP TABLE numeric_data
|
32
|
-
DROP TABLE mixed_case_monkeys
|
33
|
-
DROP TABLE minimalistics
|
34
|
-
DROP TABLE schema_info
|
35
|
-
go
|
data/test/sqlanywhere.sql
DELETED
@@ -1,222 +0,0 @@
|
|
1
|
-
CREATE TABLE accounts (
|
2
|
-
id integer DEFAULT AUTOINCREMENT NOT NULL PRIMARY KEY,
|
3
|
-
firm_id int NULL,
|
4
|
-
credit_limit int NULL
|
5
|
-
)
|
6
|
-
|
7
|
-
CREATE TABLE funny_jokes (
|
8
|
-
id integer DEFAULT AUTOINCREMENT NOT NULL PRIMARY KEY,
|
9
|
-
name varchar(50) NULL
|
10
|
-
)
|
11
|
-
|
12
|
-
CREATE TABLE companies (
|
13
|
-
id integer DEFAULT AUTOINCREMENT NOT NULL PRIMARY KEY,
|
14
|
-
type varchar(50) NULL,
|
15
|
-
ruby_type varchar(50) NULL,
|
16
|
-
firm_id int NULL,
|
17
|
-
name varchar(50) NULL,
|
18
|
-
client_of int NULL,
|
19
|
-
rating int default 1
|
20
|
-
)
|
21
|
-
|
22
|
-
|
23
|
-
CREATE TABLE topics (
|
24
|
-
id integer DEFAULT AUTOINCREMENT NOT NULL PRIMARY KEY,
|
25
|
-
title varchar(255) NULL,
|
26
|
-
author_name varchar(255) NULL,
|
27
|
-
author_email_address varchar(255) NULL,
|
28
|
-
written_on datetime NULL,
|
29
|
-
bonus_time time NULL,
|
30
|
-
last_read date NULL,
|
31
|
-
content varchar(255) NULL,
|
32
|
-
approved tinyint default 1,
|
33
|
-
replies_count int default 0,
|
34
|
-
parent_id int NULL,
|
35
|
-
type varchar(50) NULL
|
36
|
-
)
|
37
|
-
|
38
|
-
CREATE TABLE developers (
|
39
|
-
id integer DEFAULT AUTOINCREMENT NOT NULL PRIMARY KEY,
|
40
|
-
name varchar(100) NULL,
|
41
|
-
salary int default 70000,
|
42
|
-
created_at datetime NULL,
|
43
|
-
updated_at datetime NULL
|
44
|
-
)
|
45
|
-
|
46
|
-
CREATE TABLE projects (
|
47
|
-
id integer DEFAULT AUTOINCREMENT NOT NULL PRIMARY KEY,
|
48
|
-
name varchar(100) NULL,
|
49
|
-
type varchar(255) NULL
|
50
|
-
)
|
51
|
-
|
52
|
-
CREATE TABLE developers_projects (
|
53
|
-
developer_id int NOT NULL,
|
54
|
-
project_id int NOT NULL,
|
55
|
-
joined_on datetime NULL,
|
56
|
-
access_level smallint default 1
|
57
|
-
)
|
58
|
-
|
59
|
-
CREATE TABLE orders (
|
60
|
-
id integer DEFAULT AUTOINCREMENT NOT NULL PRIMARY KEY,
|
61
|
-
name varchar(100) NULL,
|
62
|
-
billing_customer_id int NULL,
|
63
|
-
shipping_customer_id int NULL
|
64
|
-
)
|
65
|
-
|
66
|
-
CREATE TABLE customers (
|
67
|
-
id integer DEFAULT AUTOINCREMENT NOT NULL PRIMARY KEY,
|
68
|
-
name varchar(100) NULL,
|
69
|
-
balance int default 0,
|
70
|
-
address_street varchar(100) NULL,
|
71
|
-
address_city varchar(100) NULL,
|
72
|
-
address_country varchar(100) NULL,
|
73
|
-
gps_location varchar(100) NULL
|
74
|
-
)
|
75
|
-
|
76
|
-
CREATE TABLE movies (
|
77
|
-
movieid integer DEFAULT AUTOINCREMENT NOT NULL PRIMARY KEY,
|
78
|
-
name varchar(100) NULL
|
79
|
-
)
|
80
|
-
|
81
|
-
CREATE TABLE subscribers (
|
82
|
-
nick varchar(100) PRIMARY KEY,
|
83
|
-
name varchar(100) NULL
|
84
|
-
)
|
85
|
-
|
86
|
-
CREATE TABLE booleantests (
|
87
|
-
id integer DEFAULT AUTOINCREMENT NOT NULL PRIMARY KEY,
|
88
|
-
value int NULL
|
89
|
-
)
|
90
|
-
|
91
|
-
CREATE TABLE auto_id_tests (
|
92
|
-
auto_id integer DEFAULT AUTOINCREMENT NOT NULL PRIMARY KEY,
|
93
|
-
value int NULL
|
94
|
-
)
|
95
|
-
|
96
|
-
CREATE TABLE entrants (
|
97
|
-
id integer DEFAULT AUTOINCREMENT NOT NULL PRIMARY KEY,
|
98
|
-
name varchar(255) NOT NULL,
|
99
|
-
course_id int NOT NULL
|
100
|
-
)
|
101
|
-
|
102
|
-
CREATE TABLE colnametests (
|
103
|
-
id integer DEFAULT AUTOINCREMENT NOT NULL PRIMARY KEY,
|
104
|
-
[references] int NOT NULL
|
105
|
-
)
|
106
|
-
|
107
|
-
CREATE TABLE mixins (
|
108
|
-
id integer DEFAULT AUTOINCREMENT NOT NULL PRIMARY KEY,
|
109
|
-
parent_id int NULL,
|
110
|
-
pos int NULL,
|
111
|
-
created_at datetime NULL,
|
112
|
-
updated_at datetime NULL,
|
113
|
-
lft int NULL,
|
114
|
-
rgt int NULL,
|
115
|
-
root_id int NULL,
|
116
|
-
type varchar(40) NULL
|
117
|
-
)
|
118
|
-
|
119
|
-
CREATE TABLE people (
|
120
|
-
id integer DEFAULT AUTOINCREMENT NOT NULL PRIMARY KEY,
|
121
|
-
first_name varchar(40) NULL,
|
122
|
-
lock_version int DEFAULT 0
|
123
|
-
)
|
124
|
-
|
125
|
-
CREATE TABLE readers (
|
126
|
-
id integer DEFAULT AUTOINCREMENT NOT NULL PRIMARY KEY,
|
127
|
-
post_id int NOT NULL,
|
128
|
-
person_id int NOT NULL
|
129
|
-
)
|
130
|
-
|
131
|
-
CREATE TABLE binaries (
|
132
|
-
id integer DEFAULT AUTOINCREMENT NOT NULL PRIMARY KEY,
|
133
|
-
data long binary NULL
|
134
|
-
)
|
135
|
-
|
136
|
-
CREATE TABLE computers (
|
137
|
-
id integer DEFAULT AUTOINCREMENT NOT NULL PRIMARY KEY,
|
138
|
-
developer int NOT NULL,
|
139
|
-
extendedWarranty int NOT NULL
|
140
|
-
)
|
141
|
-
|
142
|
-
CREATE TABLE posts (
|
143
|
-
id integer DEFAULT AUTOINCREMENT NOT NULL PRIMARY KEY,
|
144
|
-
author_id int NULL,
|
145
|
-
title varchar(255) NOT NULL,
|
146
|
-
body varchar(2048) NOT NULL,
|
147
|
-
type varchar(255) DEFAULT NULL
|
148
|
-
)
|
149
|
-
|
150
|
-
CREATE TABLE comments (
|
151
|
-
id integer DEFAULT AUTOINCREMENT NOT NULL PRIMARY KEY,
|
152
|
-
post_id int NOT NULL,
|
153
|
-
body varchar(2048) NOT NULL,
|
154
|
-
type varchar(255) NOT NULL
|
155
|
-
)
|
156
|
-
|
157
|
-
CREATE TABLE authors (
|
158
|
-
id integer DEFAULT AUTOINCREMENT NOT NULL PRIMARY KEY,
|
159
|
-
name varchar(255) NOT NULL
|
160
|
-
)
|
161
|
-
|
162
|
-
CREATE TABLE tasks (
|
163
|
-
id integer DEFAULT AUTOINCREMENT NOT NULL PRIMARY KEY,
|
164
|
-
starting datetime NULL,
|
165
|
-
ending datetime NULL
|
166
|
-
)
|
167
|
-
|
168
|
-
CREATE TABLE categories (
|
169
|
-
id integer DEFAULT AUTOINCREMENT NOT NULL PRIMARY KEY,
|
170
|
-
name varchar(255) NOT NULL,
|
171
|
-
type varchar(255) NOT NULL
|
172
|
-
)
|
173
|
-
|
174
|
-
CREATE TABLE categories_posts (
|
175
|
-
category_id int NOT NULL,
|
176
|
-
post_id int NOT NULL
|
177
|
-
)
|
178
|
-
|
179
|
-
CREATE TABLE fk_test_has_pk (
|
180
|
-
id integer DEFAULT AUTOINCREMENT NOT NULL PRIMARY KEY
|
181
|
-
)
|
182
|
-
|
183
|
-
CREATE TABLE fk_test_has_fk (
|
184
|
-
id integer PRIMARY KEY,
|
185
|
-
fk_id integer NOT NULL,
|
186
|
-
|
187
|
-
FOREIGN KEY (fk_id) REFERENCES fk_test_has_pk(id)
|
188
|
-
)
|
189
|
-
|
190
|
-
|
191
|
-
CREATE TABLE keyboards (
|
192
|
-
key_number integer DEFAULT AUTOINCREMENT NOT NULL PRIMARY KEY,
|
193
|
-
name varchar(50) NULL
|
194
|
-
)
|
195
|
-
|
196
|
-
--This table has an altered lock_version column name.
|
197
|
-
CREATE TABLE legacy_things (
|
198
|
-
id integer DEFAULT AUTOINCREMENT NOT NULL PRIMARY KEY,
|
199
|
-
tps_report_number int default NULL,
|
200
|
-
version int default 0
|
201
|
-
)
|
202
|
-
|
203
|
-
|
204
|
-
CREATE TABLE numeric_data (
|
205
|
-
id integer DEFAULT AUTOINCREMENT NOT NULL PRIMARY KEY,
|
206
|
-
bank_balance numeric(10,2),
|
207
|
-
big_bank_balance numeric(15,2),
|
208
|
-
world_population numeric(10),
|
209
|
-
my_house_population numeric(2),
|
210
|
-
decimal_number_with_default numeric(3,2) DEFAULT 2.78
|
211
|
-
)
|
212
|
-
|
213
|
-
CREATE TABLE mixed_case_monkeys (
|
214
|
-
monkeyID integer DEFAULT AUTOINCREMENT NOT NULL PRIMARY KEY,
|
215
|
-
fleaCount integer
|
216
|
-
);
|
217
|
-
|
218
|
-
CREATE TABLE minimalistics (
|
219
|
-
id integer DEFAULT AUTOINCREMENT NOT NULL PRIMARY KEY
|
220
|
-
);
|
221
|
-
|
222
|
-
go
|
data/test/sqlanywhere2.drop.sql
DELETED