activerecord-sqlanywhere-adapter 0.1.3 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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