forked from rsim/oracle-enhanced
/
quoting.rb
189 lines (169 loc) · 6.14 KB
/
quoting.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
# frozen_string_literal: true
module ActiveRecord
module ConnectionAdapters
module OracleEnhanced
module Quoting
# QUOTING ==================================================
#
# see: abstract/quoting.rb
def quote_column_name(name) #:nodoc:
name = name.to_s
self.class.quoted_column_names[name] ||= begin
# if only valid lowercase column characters in name
if /\A[a-z][a-z_0-9\$#]*\Z/.match?(name)
"\"#{name.upcase}\""
else
# remove double quotes which cannot be used inside quoted identifier
"\"#{name.gsub('"', '')}\""
end
end
end
# This method is used in add_index to identify either column name (which is quoted)
# or function based index (in which case function expression is not quoted)
def quote_column_name_or_expression(name) #:nodoc:
name = name.to_s
case name
# if only valid lowercase column characters in name
when /^[a-z][a-z_0-9\$#]*$/
"\"#{name.upcase}\""
when /^[a-z][a-z_0-9\$#\-]*$/i
"\"#{name}\""
# if other characters present then assume that it is expression
# which should not be quoted
else
name
end
end
# Names must be from 1 to 30 bytes long with these exceptions:
# * Names of databases are limited to 8 bytes.
# * Names of database links can be as long as 128 bytes.
#
# Nonquoted identifiers cannot be Oracle Database reserved words
#
# Nonquoted identifiers must begin with an alphabetic character from
# your database character set
#
# Nonquoted identifiers can contain only alphanumeric characters from
# your database character set and the underscore (_), dollar sign ($),
# and pound sign (#).
# Oracle strongly discourages you from using $ and # in nonquoted identifiers.
NONQUOTED_OBJECT_NAME = /[[:alpha:]][\w$#]{0,29}/
VALID_TABLE_NAME = /\A(?:#{NONQUOTED_OBJECT_NAME}\.)?#{NONQUOTED_OBJECT_NAME}?\Z/
# unescaped table name should start with letter and
# contain letters, digits, _, $ or #
# can be prefixed with schema name
# CamelCase table names should be quoted
def self.valid_table_name?(name) #:nodoc:
object_name = name.to_s
!!(object_name =~ VALID_TABLE_NAME && !mixed_case?(object_name))
end
def self.mixed_case?(name)
object_name = name.include?(".") ? name.split(".").second : name
!!(object_name =~ /[A-Z]/ && object_name =~ /[a-z]/)
end
def quote_table_name(name) #:nodoc:
name, _link = name.to_s.split("@")
self.class.quoted_table_names[name] ||= [name.split(".").map { |n| quote_column_name(n) }].join(".")
end
def quote_string(s) #:nodoc:
s.gsub(/'/, "''")
end
def _quote(value) #:nodoc:
case value
when Type::OracleEnhanced::CharacterString::Data then
"'#{quote_string(value.to_s)}'"
when Type::OracleEnhanced::NationalCharacterString::Data then
+"N" << "'#{quote_string(value.to_s)}'"
when ActiveModel::Type::Binary::Data then
"empty_blob()"
when Type::OracleEnhanced::Text::Data then
"empty_clob()"
when Type::OracleEnhanced::NationalCharacterText::Data then
"empty_nclob()"
else
super
end
end
def quoted_true #:nodoc:
return "'Y'" if emulate_booleans_from_strings
"1"
end
def unquoted_true #:nodoc:
return "Y" if emulate_booleans_from_strings
"1"
end
def quoted_false #:nodoc:
return "'N'" if emulate_booleans_from_strings
"0"
end
def unquoted_false #:nodoc:
return "N" if emulate_booleans_from_strings
"0"
end
def _type_cast(value)
case value
when Type::OracleEnhanced::TimestampTz::Data, Type::OracleEnhanced::TimestampLtz::Data
if value.acts_like?(:time)
zone_conversion_method = ActiveRecord::Base.default_timezone == :utc ? :getutc : :getlocal
value.respond_to?(zone_conversion_method) ? value.send(zone_conversion_method) : value
else
value
end
when Type::OracleEnhanced::NationalCharacterString::Data
value.to_s
when Type::OracleEnhanced::CharacterString::Data
value
else
super
end
end
def column_name_matcher
COLUMN_NAME
end
def column_name_with_order_matcher
COLUMN_NAME_WITH_ORDER
end
COLUMN_NAME = /
\A
(
(?:
# "table_name"."column_name" | function(one or no argument)
((?:\w+\.|"\w+"\.)?(?:\w+|"\w+")) | \w+\((?:|\g<2>)\)
)
(?:(?:\s+AS)?\s+(?:\w+|"\w+"))?
)
(?:\s*,\s*\g<1>)*
\z
/ix
COLUMN_NAME_WITH_ORDER = /
\A
(
(?:
# "table_name"."column_name" | function(one or no argument)
((?:\w+\.|"\w+"\.)?(?:\w+|"\w+")) | \w+\((?:|\g<2>)\)
)
(?:\s+ASC|\s+DESC)?
(?:\s+NULLS\s+(?:FIRST|LAST))?
)
(?:\s*,\s*\g<1>)*
\z
/ix
private_constant :COLUMN_NAME, :COLUMN_NAME_WITH_ORDER
private
def oracle_downcase(column_name)
return nil if column_name.nil?
/[a-z]/.match?(column_name) ? column_name : column_name.downcase
end
end
end
end
end
# if MRI or YARV or TruffleRuby
if !defined?(RUBY_ENGINE) || RUBY_ENGINE == "ruby" || RUBY_ENGINE == "truffleruby"
require "active_record/connection_adapters/oracle_enhanced/oci_quoting"
# if JRuby
elsif RUBY_ENGINE == "jruby"
require "active_record/connection_adapters/oracle_enhanced/jdbc_quoting"
else
raise "Unsupported Ruby engine #{RUBY_ENGINE}"
end