dm-oracle-adapter 1.0.0.rc1
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/.gitignore +36 -0
- data/Gemfile +87 -0
- data/LICENSE +20 -0
- data/Rakefile +27 -0
- data/VERSION +1 -0
- data/dm-oracle-adapter.gemspec +66 -0
- data/lib/dm-oracle-adapter.rb +1 -0
- data/lib/dm-oracle-adapter/adapter.rb +228 -0
- data/lib/dm-oracle-adapter/spec/setup.rb +18 -0
- data/spec/adapter_spec.rb +208 -0
- data/spec/rcov.opts +5 -0
- data/spec/spec.opts +3 -0
- data/spec/spec_helper.rb +5 -0
- data/tasks/local_gemfile.rake +18 -0
- data/tasks/spec.rake +38 -0
- data/tasks/yard.rake +9 -0
- data/tasks/yardstick.rake +19 -0
- metadata +123 -0
data/.gitignore
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
## MAC OS
|
|
2
|
+
.DS_Store
|
|
3
|
+
|
|
4
|
+
## TEXTMATE
|
|
5
|
+
*.tmproj
|
|
6
|
+
tmtags
|
|
7
|
+
|
|
8
|
+
## EMACS
|
|
9
|
+
*~
|
|
10
|
+
\#*
|
|
11
|
+
.\#*
|
|
12
|
+
|
|
13
|
+
## VIM
|
|
14
|
+
*.swp
|
|
15
|
+
|
|
16
|
+
## Rubinius
|
|
17
|
+
*.rbc
|
|
18
|
+
|
|
19
|
+
## PROJECT::GENERAL
|
|
20
|
+
*.gem
|
|
21
|
+
coverage
|
|
22
|
+
rdoc
|
|
23
|
+
pkg
|
|
24
|
+
tmp
|
|
25
|
+
doc
|
|
26
|
+
log
|
|
27
|
+
.yardoc
|
|
28
|
+
measurements
|
|
29
|
+
|
|
30
|
+
## BUNDLER
|
|
31
|
+
.bundle
|
|
32
|
+
Gemfile.local
|
|
33
|
+
Gemfile.lock
|
|
34
|
+
|
|
35
|
+
## PROJECT::SPECIFIC
|
|
36
|
+
spec/db/
|
data/Gemfile
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# If you're working on more than one datamapper gem at a time, then it's
|
|
2
|
+
# recommended to create a local Gemfile and use this instead of the git
|
|
3
|
+
# sources. This will make sure that you are developing against your
|
|
4
|
+
# other local datamapper sources that you currently work on. Gemfile.local
|
|
5
|
+
# will behave identically to the standard Gemfile apart from the fact that
|
|
6
|
+
# it fetches the gems from local paths. This means that you can use the
|
|
7
|
+
# same environment variables, like ADAPTER when running bundle commands.
|
|
8
|
+
# Gemfile.local is added to .gitignore, so you don't need to worry about
|
|
9
|
+
# accidentally checking local development paths into git.
|
|
10
|
+
#
|
|
11
|
+
# bundle exec rake local_gemfile
|
|
12
|
+
#
|
|
13
|
+
# will give you a Gemfile.local file that points to your local clones of
|
|
14
|
+
# the various datamapper gems. It's assumed that all datamapper repo clones
|
|
15
|
+
# reside in the same directory. You can use the Gemfile.local like so for
|
|
16
|
+
# running any bundle command:
|
|
17
|
+
#
|
|
18
|
+
# BUNDLE_GEMFILE=Gemfile.local bundle foo
|
|
19
|
+
#
|
|
20
|
+
# To speed up running bundle tasks, it's recommended to run
|
|
21
|
+
#
|
|
22
|
+
# bundle lock
|
|
23
|
+
#
|
|
24
|
+
# after running 'bundle install' for the first time. This will make 'bundle exec' run
|
|
25
|
+
# a lot faster compared to the unlocked version. With an unlocked bundle you would
|
|
26
|
+
# typically just run 'bundle install' from time to time to fetch the latest sources from
|
|
27
|
+
# upstream. When you locked your bundle, you need to run
|
|
28
|
+
#
|
|
29
|
+
# bundle install --relock
|
|
30
|
+
#
|
|
31
|
+
# to make sure to fetch the latest updates and then lock the bundle again. Gemfile.lock
|
|
32
|
+
# is added to the .gitignore file, so you don't need to worry about accidentally checking
|
|
33
|
+
# it into version control.
|
|
34
|
+
|
|
35
|
+
source 'http://rubygems.org'
|
|
36
|
+
|
|
37
|
+
DATAMAPPER = 'git://github.com/datamapper'
|
|
38
|
+
DM_VERSION = '~> 1.0.0.rc1'
|
|
39
|
+
DO_VERSION = '~> 0.10.2'
|
|
40
|
+
|
|
41
|
+
group :runtime do # Runtime dependencies (as in the gemspec)
|
|
42
|
+
|
|
43
|
+
gem 'do_oracle', DO_VERSION, :git => "#{DATAMAPPER}/do.git"
|
|
44
|
+
gem 'dm-do-adapter', DM_VERSION, :git => "#{DATAMAPPER}/dm-do-adapter.git"
|
|
45
|
+
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
group(:development) do # Development dependencies (as in the gemspec)
|
|
49
|
+
|
|
50
|
+
gem 'dm-migrations', DM_VERSION, :git => "#{DATAMAPPER}/dm-migrations.git"
|
|
51
|
+
|
|
52
|
+
gem 'rake', '~> 0.8.7'
|
|
53
|
+
gem 'rspec', '~> 1.3'
|
|
54
|
+
gem 'jeweler', '~> 1.4'
|
|
55
|
+
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
group :datamapper do # We need this because we want to pin these dependencies to their git master sources
|
|
59
|
+
|
|
60
|
+
if ENV['EXTLIB']
|
|
61
|
+
gem 'extlib', '~> 0.9.15', :git => "#{DATAMAPPER}/extlib.git", :require => nil
|
|
62
|
+
else
|
|
63
|
+
gem 'activesupport', '~> 3.0.0.beta3', :git => 'git://github.com/rails/rails.git', :require => nil
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
gem 'dm-core', DM_VERSION, :git => "#{DATAMAPPER}/dm-core.git"
|
|
67
|
+
gem 'data_objects', DO_VERSION, :git => "#{DATAMAPPER}/do.git"
|
|
68
|
+
|
|
69
|
+
plugins = ENV['PLUGINS'] || ENV['PLUGIN']
|
|
70
|
+
plugins = (plugins.to_s.gsub(',',' ').split(' ') + ['dm-migrations']).uniq
|
|
71
|
+
|
|
72
|
+
plugins.each do |plugin|
|
|
73
|
+
gem plugin, DM_VERSION, :git => "#{DATAMAPPER}/#{plugin}.git"
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
group :quality do # These gems contain rake tasks that check the quality of the source code
|
|
79
|
+
|
|
80
|
+
gem 'metric_fu', '~> 1.3'
|
|
81
|
+
gem 'rcov', '~> 0.9.7'
|
|
82
|
+
gem 'reek', '~> 1.2.7'
|
|
83
|
+
gem 'roodi', '~> 2.1'
|
|
84
|
+
gem 'yard', '~> 0.5'
|
|
85
|
+
gem 'yardstick', '~> 0.1'
|
|
86
|
+
|
|
87
|
+
end
|
data/LICENSE
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
Copyright (c) 2010 Dan Kubb
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
4
|
+
a copy of this software and associated documentation files (the
|
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
9
|
+
the following conditions:
|
|
10
|
+
|
|
11
|
+
The above copyright notice and this permission notice shall be
|
|
12
|
+
included in all copies or substantial portions of the Software.
|
|
13
|
+
|
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
require 'rubygems'
|
|
2
|
+
require 'rake'
|
|
3
|
+
|
|
4
|
+
begin
|
|
5
|
+
|
|
6
|
+
require 'jeweler'
|
|
7
|
+
|
|
8
|
+
Jeweler::Tasks.new do |gem|
|
|
9
|
+
gem.name = 'dm-oracle-adapter'
|
|
10
|
+
gem.summary = 'Oracle Adapter for DataMapper'
|
|
11
|
+
gem.description = gem.summary
|
|
12
|
+
gem.authors = ["Dan Kubb"]
|
|
13
|
+
gem.email = %q{dan.kubb@gmail.com}
|
|
14
|
+
gem.homepage = 'http://github.com/datamapper/dm-oracle-adapter'
|
|
15
|
+
|
|
16
|
+
gem.add_dependency 'do_oracle', '~> 0.10.1'
|
|
17
|
+
gem.add_dependency 'dm-do-adapter', '~> 1.0.0.rc1'
|
|
18
|
+
|
|
19
|
+
gem.add_development_dependency 'rspec', '~> 1.3'
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
Jeweler::GemcutterTasks.new
|
|
23
|
+
|
|
24
|
+
FileList['tasks/**/*.rake'].each { |task| import task }
|
|
25
|
+
rescue LoadError
|
|
26
|
+
puts 'Jeweler (or a dependency) not available. Install it with: gem install jeweler'
|
|
27
|
+
end
|
data/VERSION
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
1.0.0.rc1
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# Generated by jeweler
|
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
|
4
|
+
# -*- encoding: utf-8 -*-
|
|
5
|
+
|
|
6
|
+
Gem::Specification.new do |s|
|
|
7
|
+
s.name = %q{dm-oracle-adapter}
|
|
8
|
+
s.version = "1.0.0.rc1"
|
|
9
|
+
|
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new("> 1.3.1") if s.respond_to? :required_rubygems_version=
|
|
11
|
+
s.authors = ["Dan Kubb"]
|
|
12
|
+
s.date = %q{2010-05-19}
|
|
13
|
+
s.description = %q{Oracle Adapter for DataMapper}
|
|
14
|
+
s.email = %q{dan.kubb@gmail.com}
|
|
15
|
+
s.extra_rdoc_files = [
|
|
16
|
+
"LICENSE"
|
|
17
|
+
]
|
|
18
|
+
s.files = [
|
|
19
|
+
".gitignore",
|
|
20
|
+
"Gemfile",
|
|
21
|
+
"LICENSE",
|
|
22
|
+
"Rakefile",
|
|
23
|
+
"VERSION",
|
|
24
|
+
"dm-oracle-adapter.gemspec",
|
|
25
|
+
"lib/dm-oracle-adapter.rb",
|
|
26
|
+
"lib/dm-oracle-adapter/adapter.rb",
|
|
27
|
+
"lib/dm-oracle-adapter/spec/setup.rb",
|
|
28
|
+
"spec/adapter_spec.rb",
|
|
29
|
+
"spec/rcov.opts",
|
|
30
|
+
"spec/spec.opts",
|
|
31
|
+
"spec/spec_helper.rb",
|
|
32
|
+
"tasks/local_gemfile.rake",
|
|
33
|
+
"tasks/spec.rake",
|
|
34
|
+
"tasks/yard.rake",
|
|
35
|
+
"tasks/yardstick.rake"
|
|
36
|
+
]
|
|
37
|
+
s.homepage = %q{http://github.com/datamapper/dm-oracle-adapter}
|
|
38
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
|
39
|
+
s.require_paths = ["lib"]
|
|
40
|
+
s.rubygems_version = %q{1.3.6}
|
|
41
|
+
s.summary = %q{Oracle Adapter for DataMapper}
|
|
42
|
+
s.test_files = [
|
|
43
|
+
"spec/adapter_spec.rb",
|
|
44
|
+
"spec/spec_helper.rb"
|
|
45
|
+
]
|
|
46
|
+
|
|
47
|
+
if s.respond_to? :specification_version then
|
|
48
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
|
49
|
+
s.specification_version = 3
|
|
50
|
+
|
|
51
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
|
52
|
+
s.add_runtime_dependency(%q<do_oracle>, ["~> 0.10.1"])
|
|
53
|
+
s.add_runtime_dependency(%q<dm-do-adapter>, ["~> 1.0.0.rc1"])
|
|
54
|
+
s.add_development_dependency(%q<rspec>, ["~> 1.3"])
|
|
55
|
+
else
|
|
56
|
+
s.add_dependency(%q<do_oracle>, ["~> 0.10.1"])
|
|
57
|
+
s.add_dependency(%q<dm-do-adapter>, ["~> 1.0.0.rc1"])
|
|
58
|
+
s.add_dependency(%q<rspec>, ["~> 1.3"])
|
|
59
|
+
end
|
|
60
|
+
else
|
|
61
|
+
s.add_dependency(%q<do_oracle>, ["~> 0.10.1"])
|
|
62
|
+
s.add_dependency(%q<dm-do-adapter>, ["~> 1.0.0.rc1"])
|
|
63
|
+
s.add_dependency(%q<rspec>, ["~> 1.3"])
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
require 'dm-oracle-adapter/adapter'
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
require 'do_oracle'
|
|
2
|
+
require 'dm-do-adapter'
|
|
3
|
+
|
|
4
|
+
module DataMapper
|
|
5
|
+
|
|
6
|
+
class Property
|
|
7
|
+
# for custom sequence names
|
|
8
|
+
OPTIONS << :sequence
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
module Adapters
|
|
12
|
+
class OracleAdapter < DataObjectsAdapter
|
|
13
|
+
module SQL #:nodoc:
|
|
14
|
+
IDENTIFIER_MAX_LENGTH = 30
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
|
|
18
|
+
# Constructs INSERT statement for given query,
|
|
19
|
+
#
|
|
20
|
+
# @return [String] INSERT statement as a string
|
|
21
|
+
#
|
|
22
|
+
# @api private
|
|
23
|
+
def insert_statement(model, properties, serial)
|
|
24
|
+
statement = "INSERT INTO #{quote_name(model.storage_name(name))} "
|
|
25
|
+
|
|
26
|
+
no_properties = properties.empty?
|
|
27
|
+
custom_sequence = serial && serial.options[:sequence]
|
|
28
|
+
serial_field = serial && quote_name(serial.field)
|
|
29
|
+
|
|
30
|
+
if supports_default_values? && no_properties && !custom_sequence
|
|
31
|
+
statement << "(#{serial_field}) " if serial
|
|
32
|
+
statement << default_values_clause
|
|
33
|
+
else
|
|
34
|
+
# do not use custom sequence if identity field was assigned a value
|
|
35
|
+
if custom_sequence && properties.include?(serial)
|
|
36
|
+
custom_sequence = nil
|
|
37
|
+
end
|
|
38
|
+
statement << "("
|
|
39
|
+
if custom_sequence
|
|
40
|
+
statement << "#{serial_field}"
|
|
41
|
+
statement << ", " unless no_properties
|
|
42
|
+
end
|
|
43
|
+
statement << "#{properties.map { |property| quote_name(property.field) }.join(', ')}) "
|
|
44
|
+
statement << "VALUES ("
|
|
45
|
+
if custom_sequence
|
|
46
|
+
statement << "#{quote_name(custom_sequence)}.NEXTVAL"
|
|
47
|
+
statement << ", " unless no_properties
|
|
48
|
+
end
|
|
49
|
+
statement << "#{(['?'] * properties.size).join(', ')})"
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
if supports_returning? && serial
|
|
53
|
+
statement << returning_clause(serial)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
statement
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Oracle syntax for inserting default values
|
|
60
|
+
def default_values_clause
|
|
61
|
+
'VALUES (DEFAULT)'
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# @api private
|
|
65
|
+
def supports_returning?
|
|
66
|
+
true
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# INTO :insert_id is recognized by Oracle DataObjects driver
|
|
70
|
+
def returning_clause(serial)
|
|
71
|
+
" RETURNING #{quote_name(serial.field)} INTO :insert_id"
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Constructs SELECT statement for given query,
|
|
75
|
+
# Overrides DataObjects adapter implementation with using subquery instead of GROUP BY to get unique records
|
|
76
|
+
#
|
|
77
|
+
# @return [String] SELECT statement as a string
|
|
78
|
+
#
|
|
79
|
+
# @api private
|
|
80
|
+
def select_statement(query)
|
|
81
|
+
name = self.name
|
|
82
|
+
model = query.model
|
|
83
|
+
fields = query.fields
|
|
84
|
+
conditions = query.conditions
|
|
85
|
+
limit = query.limit
|
|
86
|
+
offset = query.offset
|
|
87
|
+
order = query.order
|
|
88
|
+
group_by = nil
|
|
89
|
+
|
|
90
|
+
# FIXME: using a boolean for qualify does not work in some cases,
|
|
91
|
+
# such as when you have a self-referrential many to many association.
|
|
92
|
+
# if you don't qualfiy the columns with a unique alias, then the
|
|
93
|
+
# SQL query will fail. This may mean though, that it might not
|
|
94
|
+
# be enough to pass in a Property, but we may need to know the
|
|
95
|
+
# table and the alias we should use for the column.
|
|
96
|
+
|
|
97
|
+
qualify = query.links.any?
|
|
98
|
+
|
|
99
|
+
if query.unique?
|
|
100
|
+
group_by = fields.select { |property| property.kind_of?(Property) }
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# create subquery to find all valid keys and then use these keys to retrive all other columns
|
|
104
|
+
use_subquery = qualify
|
|
105
|
+
no_group_by = group_by.blank?
|
|
106
|
+
no_order = order.blank?
|
|
107
|
+
|
|
108
|
+
# when we can include ROWNUM condition in main WHERE clause
|
|
109
|
+
use_simple_rownum_limit = limit && (offset||0 == 0) && no_group_by && no_order
|
|
110
|
+
|
|
111
|
+
unless (limit && limit > 1) || offset > 0 || qualify
|
|
112
|
+
# TODO: move this method to Query, so that it walks the conditions
|
|
113
|
+
# and finds an OR operator
|
|
114
|
+
|
|
115
|
+
# TODO: handle cases where two or more properties need to be
|
|
116
|
+
# used together to be unique
|
|
117
|
+
|
|
118
|
+
# if a unique property is used, and there is no OR operator, then an ORDER
|
|
119
|
+
# and LIMIT are unecessary because it should only return a single row
|
|
120
|
+
if conditions.respond_to?(:slug) && conditions.slug == :and &&
|
|
121
|
+
conditions.any? { |operand| operand.respond_to?(:slug) && operand.slug == :eql && operand.subject.respond_to?(:unique?) && operand.subject.unique? } &&
|
|
122
|
+
!conditions.any? { |operand| operand.respond_to?(:slug) && operand.slug == :or }
|
|
123
|
+
order = nil
|
|
124
|
+
no_order = true
|
|
125
|
+
limit = nil
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
conditions_statement, bind_values = conditions_statement(conditions, qualify)
|
|
130
|
+
|
|
131
|
+
model_key_column = columns_statement(model.key(name), qualify)
|
|
132
|
+
from_statement = " FROM #{quote_name(model.storage_name(name))}"
|
|
133
|
+
|
|
134
|
+
statement = "SELECT #{columns_statement(fields, qualify)}"
|
|
135
|
+
if use_subquery
|
|
136
|
+
statement << from_statement
|
|
137
|
+
statement << " WHERE (#{model_key_column}) IN"
|
|
138
|
+
statement << " (SELECT DISTINCT #{model_key_column}"
|
|
139
|
+
# do not need to do group by for uniqueness as just one row per primary key will be returned
|
|
140
|
+
no_group_by = true
|
|
141
|
+
end
|
|
142
|
+
statement << from_statement
|
|
143
|
+
statement << join_statement(query, bind_values, qualify) if qualify
|
|
144
|
+
statement << " WHERE (#{conditions_statement})" unless conditions_statement.blank?
|
|
145
|
+
if use_subquery
|
|
146
|
+
statement << ")"
|
|
147
|
+
end
|
|
148
|
+
if use_simple_rownum_limit
|
|
149
|
+
statement << " AND rownum <= ?"
|
|
150
|
+
bind_values << limit
|
|
151
|
+
end
|
|
152
|
+
statement << " GROUP BY #{columns_statement(group_by, qualify)}" unless no_group_by
|
|
153
|
+
statement << " ORDER BY #{order_statement(order, qualify)}" unless no_order
|
|
154
|
+
|
|
155
|
+
add_limit_offset!(statement, limit, offset, bind_values) unless use_simple_rownum_limit
|
|
156
|
+
|
|
157
|
+
return statement, bind_values
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
# Oracle does not support LIMIT and OFFSET
|
|
161
|
+
# Functionality is mimiced through the use of nested selects.
|
|
162
|
+
# See http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:127412348064
|
|
163
|
+
def add_limit_offset!(statement, limit, offset, bind_values)
|
|
164
|
+
positive_offset = offset > 0
|
|
165
|
+
|
|
166
|
+
if limit && positive_offset
|
|
167
|
+
statement.replace "select * from (select raw_sql_.*, rownum raw_rnum_ from (#{statement}) raw_sql_ where rownum <= ?) where raw_rnum_ > ?"
|
|
168
|
+
bind_values << offset + limit << offset
|
|
169
|
+
elsif limit
|
|
170
|
+
statement.replace "select raw_sql_.* from (#{statement}) raw_sql_ where rownum <= ?"
|
|
171
|
+
bind_values << limit
|
|
172
|
+
elsif positive_offset
|
|
173
|
+
statement.replace "select * from (select raw_sql_.*, rownum raw_rnum_ from (#{statement}) raw_sql_) where raw_rnum_ > ?"
|
|
174
|
+
bind_values << offset
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
# @api private
|
|
179
|
+
# Oracle does not allow " in table or column names therefore substitute them with underscore
|
|
180
|
+
def quote_name(name)
|
|
181
|
+
"\"#{oracle_upcase(name)[0, self.class::IDENTIFIER_MAX_LENGTH].gsub('"', '_')}\""
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
# If table or column name contains just lowercase characters then do uppercase
|
|
185
|
+
# as uppercase version will be used in Oracle data dictionary tables
|
|
186
|
+
def oracle_upcase(name)
|
|
187
|
+
name =~ /[A-Z]/ ? name : name.upcase
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
# CLOB value should be compared using DBMS_LOB.SUBSTR function
|
|
191
|
+
# NOTE: just first 32767 bytes will be compared!
|
|
192
|
+
# @api private
|
|
193
|
+
def equality_operator(property, operand)
|
|
194
|
+
if operand.nil?
|
|
195
|
+
'IS'
|
|
196
|
+
elsif property.type == Types::Text
|
|
197
|
+
'DBMS_LOB.SUBSTR(%s) = ?'
|
|
198
|
+
else
|
|
199
|
+
'='
|
|
200
|
+
end
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
# @api private
|
|
204
|
+
def include_operator(property, operand)
|
|
205
|
+
operator = case operand
|
|
206
|
+
when Array then 'IN'
|
|
207
|
+
when Range then 'BETWEEN'
|
|
208
|
+
end
|
|
209
|
+
if property.type == Types::Text
|
|
210
|
+
"DBMS_LOB.SUBSTR(%s) #{operator} ?"
|
|
211
|
+
else
|
|
212
|
+
operator
|
|
213
|
+
end
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
# @api private
|
|
217
|
+
def regexp_operator(operand)
|
|
218
|
+
'REGEXP_LIKE(%s, ?)'
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
end #module SQL
|
|
222
|
+
|
|
223
|
+
include SQL
|
|
224
|
+
end # class OracleAdapter
|
|
225
|
+
|
|
226
|
+
const_added(:OracleAdapter)
|
|
227
|
+
end # module Adapters
|
|
228
|
+
end # module DataMapper
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
require 'dm-oracle-adapter'
|
|
2
|
+
require 'dm-core/spec/setup'
|
|
3
|
+
|
|
4
|
+
module DataMapper
|
|
5
|
+
module Spec
|
|
6
|
+
module Adapters
|
|
7
|
+
|
|
8
|
+
class OracleAdapter < Adapter
|
|
9
|
+
def test_connection
|
|
10
|
+
adapter.select('SELECT 1 FROM dual')
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
use OracleAdapter
|
|
15
|
+
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
require 'dm-core/spec/shared/adapter_spec'
|
|
4
|
+
require 'dm-do-adapter/spec/shared_spec'
|
|
5
|
+
|
|
6
|
+
require 'dm-migrations'
|
|
7
|
+
require 'dm-oracle-adapter/spec/setup'
|
|
8
|
+
|
|
9
|
+
ENV['ADAPTER'] = 'oracle'
|
|
10
|
+
ENV['ADAPTER_SUPPORTS'] = 'all'
|
|
11
|
+
|
|
12
|
+
module SQLLogHelper
|
|
13
|
+
class SQLLogger
|
|
14
|
+
attr_accessor :buffer
|
|
15
|
+
def initialize
|
|
16
|
+
@buffer = ""
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def level; 0; end
|
|
20
|
+
|
|
21
|
+
def debug(string)
|
|
22
|
+
@buffer << string << "\n"
|
|
23
|
+
# puts "#{string.gsub(/\n/m,"<br/>\n")}<br/>"
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def start_sql_log!
|
|
28
|
+
return if @sql_log_on
|
|
29
|
+
@sql_log_on = true
|
|
30
|
+
@old_logger = DataObjects::Oracle.logger
|
|
31
|
+
DataObjects::Oracle.logger = SQLLogger.new
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def stop_sql_log!
|
|
35
|
+
return unless @sql_log_on
|
|
36
|
+
@sql_log_on = nil
|
|
37
|
+
DataObjects::Oracle.logger = @old_logger
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def clear_sql_log!
|
|
41
|
+
return unless @sql_log_on
|
|
42
|
+
DataObjects::Oracle.logger.buffer = ""
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def sql_log_buffer
|
|
46
|
+
DataObjects::Oracle.logger.buffer
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
describe 'DataMapper::Adapters::OracleAdapter' do
|
|
52
|
+
|
|
53
|
+
before :all do
|
|
54
|
+
@adapter = DataMapper::Spec.adapter
|
|
55
|
+
@repository = DataMapper.repository(@adapter.name)
|
|
56
|
+
|
|
57
|
+
# speed up test execution
|
|
58
|
+
@adapter.class_eval do
|
|
59
|
+
auto_migrate_with :delete # table data will be deleted instead of dropping and creating table
|
|
60
|
+
auto_migrate_reset_sequences false # primary key sequences will not be reset
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
it_should_behave_like "An Adapter"
|
|
66
|
+
it_should_behave_like "A DataObjects Adapter"
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
describe "sequences" do
|
|
70
|
+
|
|
71
|
+
include SQLLogHelper
|
|
72
|
+
|
|
73
|
+
before(:all) do
|
|
74
|
+
@auto_migrate_with = DataMapper::Adapters::OracleAdapter.auto_migrate_with
|
|
75
|
+
DataMapper::Adapters::OracleAdapter.auto_migrate_with :drop_and_create
|
|
76
|
+
start_sql_log!
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
after(:all) do
|
|
80
|
+
stop_sql_log!
|
|
81
|
+
DataMapper::Adapters::OracleAdapter.auto_migrate_with @auto_migrate_with
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
describe "create default sequence and trigger" do
|
|
85
|
+
before(:all) do
|
|
86
|
+
class ::Employee
|
|
87
|
+
include DataMapper::Resource
|
|
88
|
+
property :employee_id, Serial
|
|
89
|
+
end
|
|
90
|
+
Employee.auto_migrate!
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
after(:all) do
|
|
94
|
+
Employee.auto_migrate_down!
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
it "should not have sequence name in options" do
|
|
98
|
+
Employee.properties[:employee_id].options[:sequence].should be_nil
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
it "should create default sequence" do
|
|
102
|
+
sql_log_buffer.should =~ /CREATE SEQUENCE "EMPLOYEES_SEQ"/
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
it "should create trigger" do
|
|
106
|
+
sql_log_buffer.should =~ /CREATE OR REPLACE TRIGGER "EMPLOYEES_PKT"/
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
describe "create custom sequence" do
|
|
112
|
+
|
|
113
|
+
before(:all) do
|
|
114
|
+
class ::Employee
|
|
115
|
+
include DataMapper::Resource
|
|
116
|
+
property :employee_id, Serial, :sequence => "emp_seq"
|
|
117
|
+
end
|
|
118
|
+
Employee.auto_migrate!
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
after(:all) do
|
|
122
|
+
Employee.auto_migrate_down!
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
it "should have custom sequence name" do
|
|
126
|
+
Employee.properties[:employee_id].options[:sequence].should == "emp_seq"
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
it "should create custom sequence" do
|
|
130
|
+
sql_log_buffer.should =~ /CREATE SEQUENCE "EMP_SEQ"/
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
it "should not create trigger" do
|
|
134
|
+
sql_log_buffer.should_not =~ /TRIGGER/
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
describe "create custom sequence in non-default repository" do
|
|
140
|
+
|
|
141
|
+
before(:all) do
|
|
142
|
+
stop_sql_log!
|
|
143
|
+
DataMapper.setup :oracle, DataMapper::Repository.adapters[:default].options
|
|
144
|
+
start_sql_log!
|
|
145
|
+
class ::Employee
|
|
146
|
+
include DataMapper::Resource
|
|
147
|
+
property :id, Serial
|
|
148
|
+
repository(:oracle) do
|
|
149
|
+
property :id, Serial, :field => "employee_id", :sequence => "emp_seq"
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
repository(:oracle) do
|
|
153
|
+
Employee.auto_migrate!
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
after(:all) do
|
|
158
|
+
repository(:oracle) do
|
|
159
|
+
Employee.auto_migrate_down!
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
it "should have custom sequence name" do
|
|
164
|
+
Employee.properties(:oracle)[:id].options[:sequence].should == "emp_seq"
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
it "should create custom sequence" do
|
|
168
|
+
sql_log_buffer.should =~ /CREATE SEQUENCE "EMP_SEQ"/
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
it "should not create trigger" do
|
|
172
|
+
sql_log_buffer.should_not =~ /TRIGGER/
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
describe "prefetch key value from custom sequence" do
|
|
178
|
+
|
|
179
|
+
before(:all) do
|
|
180
|
+
class ::Employee
|
|
181
|
+
include DataMapper::Resource
|
|
182
|
+
property :employee_id, Serial, :sequence => "emp_seq"
|
|
183
|
+
property :first_name, String
|
|
184
|
+
end
|
|
185
|
+
Employee.auto_migrate!
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
after(:all) do
|
|
189
|
+
Employee.auto_migrate_down!
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
it "should prefetch sequence value when inserting new record" do
|
|
193
|
+
e = Employee.create
|
|
194
|
+
e.employee_id.should == 1
|
|
195
|
+
e = Employee.create(:first_name => "Raimonds")
|
|
196
|
+
e.employee_id.should == 2
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
it "should insert explicitly specified primary key value" do
|
|
200
|
+
e = Employee.create(:employee_id => 100,:first_name => "Raimonds")
|
|
201
|
+
e.employee_id.should == 100
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
end
|
|
208
|
+
end
|
data/spec/rcov.opts
ADDED
data/spec/spec.opts
ADDED
data/spec/spec_helper.rb
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
desc "Support bundling from local source code (allows BUNDLE_GEMFILE=Gemfile.local bundle foo)"
|
|
2
|
+
task :local_gemfile do |t|
|
|
3
|
+
|
|
4
|
+
root = Pathname(__FILE__).dirname.parent
|
|
5
|
+
datamapper = root.parent
|
|
6
|
+
|
|
7
|
+
source_regex = /DATAMAPPER = 'git:\/\/github.com\/datamapper'/
|
|
8
|
+
gem_source_regex = /:git => \"#\{DATAMAPPER\}\/(.+?)(?:\.git)?\"/
|
|
9
|
+
|
|
10
|
+
root.join('Gemfile.local').open('w') do |f|
|
|
11
|
+
root.join('Gemfile').open.each do |line|
|
|
12
|
+
line.sub!(source_regex, "DATAMAPPER = '#{datamapper}'")
|
|
13
|
+
line.sub!(gem_source_regex, ':path => "#{DATAMAPPER}/\1"')
|
|
14
|
+
f.puts line
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
end
|
data/tasks/spec.rake
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
spec_defaults = lambda do |spec|
|
|
2
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
|
3
|
+
spec.libs << 'lib' << 'spec'
|
|
4
|
+
spec.spec_opts << '--options' << 'spec/spec.opts'
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
begin
|
|
8
|
+
require 'spec/rake/spectask'
|
|
9
|
+
|
|
10
|
+
Spec::Rake::SpecTask.new(:spec, &spec_defaults)
|
|
11
|
+
rescue LoadError
|
|
12
|
+
task :spec do
|
|
13
|
+
abort 'rspec is not available. In order to run spec, you must: gem install rspec'
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
begin
|
|
18
|
+
require 'rcov'
|
|
19
|
+
require 'spec/rake/verify_rcov'
|
|
20
|
+
|
|
21
|
+
Spec::Rake::SpecTask.new(:rcov) do |rcov|
|
|
22
|
+
spec_defaults.call(rcov)
|
|
23
|
+
rcov.rcov = true
|
|
24
|
+
rcov.rcov_opts = File.read('spec/rcov.opts').split(/\s+/)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
RCov::VerifyTask.new(:verify_rcov => :rcov) do |rcov|
|
|
28
|
+
rcov.threshold = 100
|
|
29
|
+
end
|
|
30
|
+
rescue LoadError
|
|
31
|
+
%w[ rcov verify_rcov ].each do |name|
|
|
32
|
+
task name do
|
|
33
|
+
abort "rcov is not available. In order to run #{name}, you must: gem install rcov"
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
task :default => :spec
|
data/tasks/yard.rake
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
begin
|
|
2
|
+
require 'pathname'
|
|
3
|
+
require 'yardstick/rake/measurement'
|
|
4
|
+
require 'yardstick/rake/verify'
|
|
5
|
+
|
|
6
|
+
# yardstick_measure task
|
|
7
|
+
Yardstick::Rake::Measurement.new
|
|
8
|
+
|
|
9
|
+
# verify_measurements task
|
|
10
|
+
Yardstick::Rake::Verify.new do |verify|
|
|
11
|
+
verify.threshold = 100
|
|
12
|
+
end
|
|
13
|
+
rescue LoadError
|
|
14
|
+
%w[ yardstick_measure verify_measurements ].each do |name|
|
|
15
|
+
task name.to_s do
|
|
16
|
+
abort "Yardstick is not available. In order to run #{name}, you must: gem install yardstick"
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: dm-oracle-adapter
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
prerelease: true
|
|
5
|
+
segments:
|
|
6
|
+
- 1
|
|
7
|
+
- 0
|
|
8
|
+
- 0
|
|
9
|
+
- rc1
|
|
10
|
+
version: 1.0.0.rc1
|
|
11
|
+
platform: ruby
|
|
12
|
+
authors:
|
|
13
|
+
- Dan Kubb
|
|
14
|
+
autorequire:
|
|
15
|
+
bindir: bin
|
|
16
|
+
cert_chain: []
|
|
17
|
+
|
|
18
|
+
date: 2010-05-19 00:00:00 -07:00
|
|
19
|
+
default_executable:
|
|
20
|
+
dependencies:
|
|
21
|
+
- !ruby/object:Gem::Dependency
|
|
22
|
+
name: do_oracle
|
|
23
|
+
prerelease: false
|
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
|
25
|
+
requirements:
|
|
26
|
+
- - ~>
|
|
27
|
+
- !ruby/object:Gem::Version
|
|
28
|
+
segments:
|
|
29
|
+
- 0
|
|
30
|
+
- 10
|
|
31
|
+
- 1
|
|
32
|
+
version: 0.10.1
|
|
33
|
+
type: :runtime
|
|
34
|
+
version_requirements: *id001
|
|
35
|
+
- !ruby/object:Gem::Dependency
|
|
36
|
+
name: dm-do-adapter
|
|
37
|
+
prerelease: false
|
|
38
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
|
39
|
+
requirements:
|
|
40
|
+
- - ~>
|
|
41
|
+
- !ruby/object:Gem::Version
|
|
42
|
+
segments:
|
|
43
|
+
- 1
|
|
44
|
+
- 0
|
|
45
|
+
- 0
|
|
46
|
+
- rc1
|
|
47
|
+
version: 1.0.0.rc1
|
|
48
|
+
type: :runtime
|
|
49
|
+
version_requirements: *id002
|
|
50
|
+
- !ruby/object:Gem::Dependency
|
|
51
|
+
name: rspec
|
|
52
|
+
prerelease: false
|
|
53
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
|
54
|
+
requirements:
|
|
55
|
+
- - ~>
|
|
56
|
+
- !ruby/object:Gem::Version
|
|
57
|
+
segments:
|
|
58
|
+
- 1
|
|
59
|
+
- 3
|
|
60
|
+
version: "1.3"
|
|
61
|
+
type: :development
|
|
62
|
+
version_requirements: *id003
|
|
63
|
+
description: Oracle Adapter for DataMapper
|
|
64
|
+
email: dan.kubb@gmail.com
|
|
65
|
+
executables: []
|
|
66
|
+
|
|
67
|
+
extensions: []
|
|
68
|
+
|
|
69
|
+
extra_rdoc_files:
|
|
70
|
+
- LICENSE
|
|
71
|
+
files:
|
|
72
|
+
- .gitignore
|
|
73
|
+
- Gemfile
|
|
74
|
+
- LICENSE
|
|
75
|
+
- Rakefile
|
|
76
|
+
- VERSION
|
|
77
|
+
- dm-oracle-adapter.gemspec
|
|
78
|
+
- lib/dm-oracle-adapter.rb
|
|
79
|
+
- lib/dm-oracle-adapter/adapter.rb
|
|
80
|
+
- lib/dm-oracle-adapter/spec/setup.rb
|
|
81
|
+
- spec/adapter_spec.rb
|
|
82
|
+
- spec/rcov.opts
|
|
83
|
+
- spec/spec.opts
|
|
84
|
+
- spec/spec_helper.rb
|
|
85
|
+
- tasks/local_gemfile.rake
|
|
86
|
+
- tasks/spec.rake
|
|
87
|
+
- tasks/yard.rake
|
|
88
|
+
- tasks/yardstick.rake
|
|
89
|
+
has_rdoc: true
|
|
90
|
+
homepage: http://github.com/datamapper/dm-oracle-adapter
|
|
91
|
+
licenses: []
|
|
92
|
+
|
|
93
|
+
post_install_message:
|
|
94
|
+
rdoc_options:
|
|
95
|
+
- --charset=UTF-8
|
|
96
|
+
require_paths:
|
|
97
|
+
- lib
|
|
98
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
99
|
+
requirements:
|
|
100
|
+
- - ">="
|
|
101
|
+
- !ruby/object:Gem::Version
|
|
102
|
+
segments:
|
|
103
|
+
- 0
|
|
104
|
+
version: "0"
|
|
105
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
106
|
+
requirements:
|
|
107
|
+
- - ">"
|
|
108
|
+
- !ruby/object:Gem::Version
|
|
109
|
+
segments:
|
|
110
|
+
- 1
|
|
111
|
+
- 3
|
|
112
|
+
- 1
|
|
113
|
+
version: 1.3.1
|
|
114
|
+
requirements: []
|
|
115
|
+
|
|
116
|
+
rubyforge_project:
|
|
117
|
+
rubygems_version: 1.3.6
|
|
118
|
+
signing_key:
|
|
119
|
+
specification_version: 3
|
|
120
|
+
summary: Oracle Adapter for DataMapper
|
|
121
|
+
test_files:
|
|
122
|
+
- spec/adapter_spec.rb
|
|
123
|
+
- spec/spec_helper.rb
|