activerecord-cubrid2-adapter 0.0.1
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.
- checksums.yaml +7 -0
- data/.gitignore +19 -0
- data/LICENSE +21 -0
- data/README.md +63 -0
- data/Rakefile +11 -0
- data/VERSION +1 -0
- data/activerecord-cubrid2-adapter.gemspec +26 -0
- data/lib/active_record/connection_adapters/abstract_cubrid2_adapter.rb +750 -0
- data/lib/active_record/connection_adapters/cubrid2/column.rb +28 -0
- data/lib/active_record/connection_adapters/cubrid2/database_statements.rb +192 -0
- data/lib/active_record/connection_adapters/cubrid2/explain_pretty_printer.rb +71 -0
- data/lib/active_record/connection_adapters/cubrid2/quoting.rb +118 -0
- data/lib/active_record/connection_adapters/cubrid2/schema_creation.rb +98 -0
- data/lib/active_record/connection_adapters/cubrid2/schema_definitions.rb +81 -0
- data/lib/active_record/connection_adapters/cubrid2/schema_dumper.rb +31 -0
- data/lib/active_record/connection_adapters/cubrid2/schema_statements.rb +276 -0
- data/lib/active_record/connection_adapters/cubrid2/type_metadata.rb +31 -0
- data/lib/active_record/connection_adapters/cubrid2/version.rb +7 -0
- data/lib/active_record/connection_adapters/cubrid2_adapter.rb +169 -0
- data/lib/activerecord-cubrid2-adapter.rb +4 -0
- data/lib/arel/visitors/cubrid.rb +67 -0
- data/lib/cubrid2/client.rb +93 -0
- data/lib/cubrid2/console.rb +5 -0
- data/lib/cubrid2/error.rb +86 -0
- data/lib/cubrid2/field.rb +3 -0
- data/lib/cubrid2/result.rb +7 -0
- data/lib/cubrid2/statement.rb +11 -0
- data/lib/cubrid2/version.rb +3 -0
- data/lib/cubrid2.rb +76 -0
- data/tests/Gemfile +10 -0
- data/tests/test_activerecord.rb +109 -0
- metadata +102 -0
@@ -0,0 +1,276 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
module ConnectionAdapters
|
5
|
+
module Cubrid2
|
6
|
+
module SchemaStatements # :nodoc:
|
7
|
+
# Returns an array of indexes for the given table.
|
8
|
+
def indexes(table_name)
|
9
|
+
indexes = []
|
10
|
+
current_index = nil
|
11
|
+
execute_and_free("SHOW KEYS FROM #{quote_table_name(table_name)}", 'SCHEMA') do |result|
|
12
|
+
each_hash(result) do |row|
|
13
|
+
if current_index != row[:Key_name]
|
14
|
+
next if row[:Key_name] == 'PRIMARY' # skip the primary key
|
15
|
+
|
16
|
+
current_index = row[:Key_name]
|
17
|
+
|
18
|
+
cubrid_index_type = row[:Index_type].downcase.to_sym
|
19
|
+
|
20
|
+
# currently only support btree
|
21
|
+
# https://www.cubrid.org/manual/en/11.2/sql/query/show.html?highlight=show%20index#show-index
|
22
|
+
index_using = cubrid_index_type
|
23
|
+
index_type = nil
|
24
|
+
|
25
|
+
indexes << [
|
26
|
+
row[:Table],
|
27
|
+
row[:Key_name],
|
28
|
+
row[:Non_unique].to_i == 0,
|
29
|
+
[],
|
30
|
+
{ lengths: {},
|
31
|
+
orders: {},
|
32
|
+
type: index_type,
|
33
|
+
using: index_using,
|
34
|
+
comment: row[:Comment].presence,
|
35
|
+
null: row[:Null] == 'YES',
|
36
|
+
visible: row[:Visible] == 'YES' }
|
37
|
+
]
|
38
|
+
end
|
39
|
+
|
40
|
+
if row[:Func]
|
41
|
+
expression = row[:Func]
|
42
|
+
expression = +"(#{expression})" unless expression.start_with?('(')
|
43
|
+
indexes.last[-2] << expression
|
44
|
+
indexes.last[-1][:expressions] ||= {}
|
45
|
+
indexes.last[-1][:expressions][expression] = expression
|
46
|
+
indexes.last[-1][:orders][expression] = :desc if row[:Collation] == 'D'
|
47
|
+
else
|
48
|
+
indexes.last[-2] << row[:Column_name]
|
49
|
+
indexes.last[-1][:lengths][row[:Column_name]] = row[:Sub_part].to_i if row[:Sub_part]
|
50
|
+
indexes.last[-1][:orders][row[:Column_name]] = :desc if row[:Collation] == 'D'
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
indexes.map do |index|
|
56
|
+
options = index.pop
|
57
|
+
|
58
|
+
if expressions = options.delete(:expressions)
|
59
|
+
orders = options.delete(:orders)
|
60
|
+
lengths = options.delete(:lengths)
|
61
|
+
|
62
|
+
columns = index[-1].map do |name|
|
63
|
+
[name.to_sym, expressions[name] || +quote_column_name(name)]
|
64
|
+
end.to_h
|
65
|
+
|
66
|
+
index[-1] = add_options_for_index_columns(
|
67
|
+
columns, order: orders, length: lengths
|
68
|
+
).values.join(', ')
|
69
|
+
end
|
70
|
+
|
71
|
+
IndexDefinition.new(*index, **options)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def remove_column(table_name, column_name, type = nil, **options)
|
76
|
+
remove_foreign_key(table_name, column: column_name) if foreign_key_exists?(table_name, column: column_name)
|
77
|
+
super
|
78
|
+
end
|
79
|
+
|
80
|
+
def create_table(table_name, options: default_row_format, **)
|
81
|
+
super
|
82
|
+
end
|
83
|
+
|
84
|
+
def internal_string_options_for_primary_key
|
85
|
+
super.tap do |options|
|
86
|
+
if !row_format_dynamic_by_default? && charset =~ /^utf8/
|
87
|
+
options[:collation] = collation.sub(/\A[^_]+/, 'utf8')
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def update_table_definition(table_name, base)
|
93
|
+
Cubrid2::Table.new(table_name, base)
|
94
|
+
end
|
95
|
+
|
96
|
+
def create_schema_dumper(options)
|
97
|
+
Cubrid2::SchemaDumper.create(self, options)
|
98
|
+
end
|
99
|
+
|
100
|
+
# Maps logical Rails types to Cubrid-specific data types.
|
101
|
+
def type_to_sql(type, limit: nil,
|
102
|
+
precision: nil, scale: nil,
|
103
|
+
size: limit_to_size(limit, type),
|
104
|
+
unsigned: nil, **)
|
105
|
+
|
106
|
+
case type.to_s
|
107
|
+
when 'integer'
|
108
|
+
integer_to_sql(limit)
|
109
|
+
when 'serial'
|
110
|
+
integer_to_sql(8) #bigint
|
111
|
+
when 'float', 'real', 'double', 'double precision'
|
112
|
+
float_to_sql(limit)
|
113
|
+
when 'text', 'string', 'varchar', 'char varing'
|
114
|
+
type_with_size_to_sql('string', size)
|
115
|
+
when 'char', 'character'
|
116
|
+
type_with_size_to_sql('char', size)
|
117
|
+
when 'blob', 'binary'
|
118
|
+
type_with_size_to_sql('blob', size)
|
119
|
+
when 'clob'
|
120
|
+
type_with_size_to_sql('clob', size)
|
121
|
+
when 'nchar', 'nchar varing'
|
122
|
+
raise 'Not supported from cubrid 9.0'
|
123
|
+
else
|
124
|
+
super
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def table_alias_length
|
129
|
+
# https://www.cubrid.org/manual/en/9.1.0/sql/identifier.html#id2
|
130
|
+
222
|
131
|
+
end
|
132
|
+
|
133
|
+
private
|
134
|
+
|
135
|
+
def row_format_dynamic_by_default?
|
136
|
+
false
|
137
|
+
end
|
138
|
+
|
139
|
+
def default_row_format
|
140
|
+
return if row_format_dynamic_by_default?
|
141
|
+
|
142
|
+
nil
|
143
|
+
end
|
144
|
+
|
145
|
+
def schema_creation
|
146
|
+
Cubrid2::SchemaCreation.new(self)
|
147
|
+
end
|
148
|
+
|
149
|
+
def create_table_definition(*args, **options)
|
150
|
+
Cubrid2::TableDefinition.new(self, *args, **options)
|
151
|
+
end
|
152
|
+
|
153
|
+
def new_column_from_field(_table_name, field)
|
154
|
+
type_metadata = fetch_type_metadata(field[:Type], field[:Extra])
|
155
|
+
default = field[:Default]
|
156
|
+
default_function = nil
|
157
|
+
|
158
|
+
if type_metadata.type == :datetime # && /\ACURRENT_TIMESTAMP(?:\([0-6]?\))?\z/i.match?(default)
|
159
|
+
default_function = default
|
160
|
+
default = nil
|
161
|
+
end
|
162
|
+
|
163
|
+
Cubrid2::Column.new(
|
164
|
+
field[:Field],
|
165
|
+
default,
|
166
|
+
type_metadata,
|
167
|
+
field[:Null] == 'YES',
|
168
|
+
default_function,
|
169
|
+
collation: field[:Collation],
|
170
|
+
comment: field[:Comment].presence,
|
171
|
+
extra: field[:Extra]
|
172
|
+
)
|
173
|
+
end
|
174
|
+
|
175
|
+
def fetch_type_metadata(sql_type, extra = '')
|
176
|
+
Cubrid2::TypeMetadata.new(super(sql_type), extra: extra)
|
177
|
+
end
|
178
|
+
|
179
|
+
def extract_foreign_key_action(specifier)
|
180
|
+
case specifier
|
181
|
+
when 'CASCADE' then :cascade
|
182
|
+
when 'SET NULL' then :nullify
|
183
|
+
when 'RESTRICT' then :restrict
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
def add_index_length(quoted_columns, **options)
|
188
|
+
lengths = options_for_index_columns(options[:length])
|
189
|
+
quoted_columns.each do |name, column|
|
190
|
+
column << "(#{lengths[name]})" if lengths[name].present?
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
def add_options_for_index_columns(quoted_columns, **options)
|
195
|
+
quoted_columns = add_index_length(quoted_columns, **options)
|
196
|
+
super
|
197
|
+
end
|
198
|
+
|
199
|
+
def data_source_sql(name = nil, type: nil)
|
200
|
+
scope = quoted_scope(name, type: type)
|
201
|
+
sql = +'SHOW TABLES '
|
202
|
+
sql << " LIKE #{scope[:name]}" if scope[:name]
|
203
|
+
sql
|
204
|
+
end
|
205
|
+
|
206
|
+
def quoted_scope(name = nil, type: nil)
|
207
|
+
schema, name = extract_schema_qualified_name(name)
|
208
|
+
scope = {}
|
209
|
+
scope[:schema] = schema ? quote(schema) : 'database()'
|
210
|
+
scope[:name] = quote(name) if name
|
211
|
+
scope[:type] = quote(type) if type
|
212
|
+
scope
|
213
|
+
end
|
214
|
+
|
215
|
+
def extract_schema_qualified_name(string)
|
216
|
+
return [] if string.nil?
|
217
|
+
|
218
|
+
q1 = '[`\"\[]'
|
219
|
+
q2 = '[`\"\]]'
|
220
|
+
schema, name = string.scan(/[^`"\[\].]+|#{q1}[^"]*#{q2}/)
|
221
|
+
if name.nil?
|
222
|
+
name = schema
|
223
|
+
schema = nil
|
224
|
+
end
|
225
|
+
[schema, name]
|
226
|
+
end
|
227
|
+
|
228
|
+
def type_with_size_to_sql(type, _size)
|
229
|
+
case type
|
230
|
+
when 'string'
|
231
|
+
'varchar'
|
232
|
+
when 'char'
|
233
|
+
'char'
|
234
|
+
when 'blob'
|
235
|
+
'blob'
|
236
|
+
when 'clob'
|
237
|
+
'clob'
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
def limit_to_size(limit, type)
|
242
|
+
case type.to_s
|
243
|
+
when 'text', 'blob', 'binary'
|
244
|
+
case limit
|
245
|
+
when 0..0xff then 'tiny'
|
246
|
+
when nil, 0x100..0xffff then nil
|
247
|
+
when 0x10000..0xffffff then 'medium'
|
248
|
+
when 0x1000000..0xffffffff then 'long'
|
249
|
+
else raise ArgumentError, "No #{type} type has byte size #{limit}"
|
250
|
+
end
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
def integer_to_sql(limit)
|
255
|
+
case limit
|
256
|
+
when 1 then 'smallint'
|
257
|
+
when 2 then 'smallint'
|
258
|
+
when 3 then 'int'
|
259
|
+
when nil, 4 then 'int'
|
260
|
+
when 5..8 then 'bigint'
|
261
|
+
when 9..16 then 'decimal'
|
262
|
+
else raise ArgumentError, "No integer type has byte size #{limit}. Use a decimal with scale 0 instead."
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
def float_to_sql(limit)
|
267
|
+
case limit
|
268
|
+
when nil, 1..4 then 'float'
|
269
|
+
when 5..8 then 'double'
|
270
|
+
else raise ArgumentError, "No float type has byte size #{limit}. Use a decimal with scale 0 instead."
|
271
|
+
end
|
272
|
+
end
|
273
|
+
end
|
274
|
+
end
|
275
|
+
end
|
276
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
module ConnectionAdapters
|
5
|
+
module Cubrid2
|
6
|
+
class TypeMetadata < DelegateClass(SqlTypeMetadata) # :nodoc:
|
7
|
+
undef to_yaml if method_defined?(:to_yaml)
|
8
|
+
|
9
|
+
attr_reader :extra
|
10
|
+
|
11
|
+
def initialize(type_metadata, extra: '')
|
12
|
+
super(type_metadata)
|
13
|
+
@extra = extra
|
14
|
+
end
|
15
|
+
|
16
|
+
def ==(other)
|
17
|
+
other.is_a?(TypeMetadata) &&
|
18
|
+
__getobj__ == other.__getobj__ &&
|
19
|
+
extra == other.extra
|
20
|
+
end
|
21
|
+
alias eql? ==
|
22
|
+
|
23
|
+
def hash
|
24
|
+
TypeMetadata.hash ^
|
25
|
+
__getobj__.hash ^
|
26
|
+
extra.hash
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,169 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'active_record/connection_adapters/abstract_cubrid2_adapter'
|
4
|
+
require 'active_record/connection_adapters/cubrid2/database_statements'
|
5
|
+
require 'cubrid2'
|
6
|
+
|
7
|
+
module ActiveRecord
|
8
|
+
module ConnectionHandling # :nodoc:
|
9
|
+
ER_DATABASE_CONNECTION_ERROR = -1000
|
10
|
+
|
11
|
+
# Establishes a connection to the database that's used by all Active Record objects.
|
12
|
+
def cubrid2_connection(config)
|
13
|
+
config = config.symbolize_keys
|
14
|
+
config[:flags] ||= 0
|
15
|
+
|
16
|
+
client = Cubrid2::Client.new(config)
|
17
|
+
ConnectionAdapters::Cubrid2Adapter.new(client, logger, nil, config)
|
18
|
+
rescue Cubrid2::Error => e
|
19
|
+
if e.error_number == ER_DATABASE_CONNECTION_ERROR
|
20
|
+
raise ActiveRecord::NoDatabaseError
|
21
|
+
else
|
22
|
+
raise
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
module ConnectionAdapters
|
28
|
+
class Cubrid2Adapter < AbstractCubrid2Adapter
|
29
|
+
ADAPTER_NAME = 'Cubrid2'
|
30
|
+
|
31
|
+
include Cubrid2::DatabaseStatements
|
32
|
+
|
33
|
+
def initialize(connection, logger, connection_options, config)
|
34
|
+
superclass_config = config.reverse_merge(prepared_statements: false)
|
35
|
+
super(connection, logger, connection_options, superclass_config)
|
36
|
+
configure_connection
|
37
|
+
end
|
38
|
+
|
39
|
+
def adapter_name
|
40
|
+
ADAPTER_NAME
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.database_exists?(config)
|
44
|
+
!!ActiveRecord::Base.cubrid_connection(config)
|
45
|
+
rescue ActiveRecord::NoDatabaseError
|
46
|
+
false
|
47
|
+
end
|
48
|
+
|
49
|
+
def supports_json?
|
50
|
+
database_version >= '10.2'
|
51
|
+
end
|
52
|
+
|
53
|
+
def supports_comments?
|
54
|
+
# https://www.cubrid.org/manual/en/10.0/release_note/r10_0.html#overview
|
55
|
+
database_version >= '10.0'
|
56
|
+
end
|
57
|
+
|
58
|
+
def supports_comments_in_create?
|
59
|
+
supports_comments?
|
60
|
+
end
|
61
|
+
|
62
|
+
def supports_savepoints?
|
63
|
+
true
|
64
|
+
end
|
65
|
+
|
66
|
+
def supports_lazy_transactions?
|
67
|
+
false
|
68
|
+
end
|
69
|
+
|
70
|
+
# HELPER METHODS ===========================================
|
71
|
+
def each_hash(result) # :nodoc:
|
72
|
+
stmt = result.is_a?(Array) ? result.first : result
|
73
|
+
if block_given?
|
74
|
+
if result && stmt
|
75
|
+
while row = stmt.fetch_hash
|
76
|
+
yield row.symbolize_keys
|
77
|
+
end
|
78
|
+
end
|
79
|
+
else
|
80
|
+
to_enum(:each_hash, stmt)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def error_number(exception)
|
85
|
+
exception.error_number if exception.respond_to?(:error_number)
|
86
|
+
end
|
87
|
+
|
88
|
+
#--
|
89
|
+
# QUOTING ==================================================
|
90
|
+
#++
|
91
|
+
|
92
|
+
def quote_string(string)
|
93
|
+
# escaping with backslash is only allowed when 'no_backslash_escapes' == 'yes' in cubrid config, default is yes.
|
94
|
+
# See: https://www.cubrid.org/manual/ko/11.2/sql/literal.html#id5
|
95
|
+
# "'#{string.gsub("'", "''")}'"
|
96
|
+
string
|
97
|
+
end
|
98
|
+
|
99
|
+
#--
|
100
|
+
# CONNECTION MANAGEMENT ====================================
|
101
|
+
#++
|
102
|
+
|
103
|
+
def active?
|
104
|
+
@connection.ping
|
105
|
+
end
|
106
|
+
|
107
|
+
def reconnect!
|
108
|
+
super
|
109
|
+
disconnect!
|
110
|
+
connect
|
111
|
+
end
|
112
|
+
alias reset! reconnect!
|
113
|
+
|
114
|
+
# Disconnects from the database if already connected.
|
115
|
+
# Otherwise, this method does nothing.
|
116
|
+
def disconnect!
|
117
|
+
super
|
118
|
+
@connection.close
|
119
|
+
end
|
120
|
+
|
121
|
+
def discard! # :nodoc:
|
122
|
+
super
|
123
|
+
#@connection.automatic_close = false
|
124
|
+
@connection = nil
|
125
|
+
end
|
126
|
+
|
127
|
+
def server_version
|
128
|
+
@connection.server_version
|
129
|
+
end
|
130
|
+
|
131
|
+
def ping
|
132
|
+
@connection.ping
|
133
|
+
end
|
134
|
+
|
135
|
+
def cubrid_connection
|
136
|
+
@connection
|
137
|
+
end
|
138
|
+
|
139
|
+
# 오류??
|
140
|
+
def auto_commit
|
141
|
+
@connection.auto_commit
|
142
|
+
end
|
143
|
+
|
144
|
+
def auto_commit=(flag)
|
145
|
+
@connection.auto_commit = flag
|
146
|
+
end
|
147
|
+
|
148
|
+
private
|
149
|
+
|
150
|
+
def connect
|
151
|
+
@connection = Cubrid2::Client.new(@config)
|
152
|
+
configure_connection
|
153
|
+
end
|
154
|
+
|
155
|
+
def configure_connection
|
156
|
+
@connection.query_options[:as] = :array
|
157
|
+
super
|
158
|
+
end
|
159
|
+
|
160
|
+
def full_version
|
161
|
+
schema_cache.database_version.full_version_string
|
162
|
+
end
|
163
|
+
|
164
|
+
def get_full_version
|
165
|
+
@connection.server_version
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module Arel # :nodoc: all
|
2
|
+
module Visitors
|
3
|
+
class Cubrid < Arel::Visitors::ToSql
|
4
|
+
private
|
5
|
+
|
6
|
+
def visit_Arel_Nodes_Bin(o, collector)
|
7
|
+
collector << 'BINARY '
|
8
|
+
visit o.expr, collector
|
9
|
+
end
|
10
|
+
|
11
|
+
def visit_Arel_Nodes_UnqualifiedColumn(o, collector)
|
12
|
+
visit o.expr, collector
|
13
|
+
end
|
14
|
+
|
15
|
+
def visit_Arel_Nodes_SelectCore(o, collector)
|
16
|
+
o.froms ||= Arel.sql('DB_ROOT')
|
17
|
+
super
|
18
|
+
end
|
19
|
+
|
20
|
+
def visit_Arel_Nodes_Concat(o, collector)
|
21
|
+
collector << ' CONCAT('
|
22
|
+
visit o.left, collector
|
23
|
+
collector << ', '
|
24
|
+
visit o.right, collector
|
25
|
+
collector << ') '
|
26
|
+
collector
|
27
|
+
end
|
28
|
+
|
29
|
+
def visit_Arel_Nodes_IsNotDistinctFrom(o, collector)
|
30
|
+
collector = visit o.left, collector
|
31
|
+
collector << ' <=> '
|
32
|
+
visit o.right, collector
|
33
|
+
end
|
34
|
+
|
35
|
+
def visit_Arel_Nodes_IsDistinctFrom(o, collector)
|
36
|
+
collector << 'NOT '
|
37
|
+
visit_Arel_Nodes_IsNotDistinctFrom o, collector
|
38
|
+
end
|
39
|
+
|
40
|
+
def visit_Arel_Nodes_Regexp(o, collector)
|
41
|
+
infix_value o, collector, ' REGEXP '
|
42
|
+
end
|
43
|
+
|
44
|
+
def visit_Arel_Nodes_NotRegexp(o, collector)
|
45
|
+
infix_value o, collector, ' NOT REGEXP '
|
46
|
+
end
|
47
|
+
|
48
|
+
# no-op
|
49
|
+
def visit_Arel_Nodes_NullsFirst(o, collector)
|
50
|
+
visit o.expr, collector
|
51
|
+
end
|
52
|
+
|
53
|
+
# In the simple case, Cubrid allows us to place JOINs directly into the UPDATE
|
54
|
+
# query. However, this does not allow for LIMIT, OFFSET and ORDER. To support
|
55
|
+
# these, we must use a subquery.
|
56
|
+
def prepare_update_statement(o)
|
57
|
+
if o.offset # || has_group_by_and_having?(o) ||
|
58
|
+
has_join_sources?(o) && has_limit_or_offset_or_orders?(o)
|
59
|
+
super
|
60
|
+
else
|
61
|
+
o
|
62
|
+
end
|
63
|
+
end
|
64
|
+
alias prepare_delete_statement prepare_update_statement
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
module Cubrid2
|
2
|
+
class Client
|
3
|
+
delegate :new, :prepare, to: :@conn
|
4
|
+
|
5
|
+
attr_reader :query_options, :read_timeout, :conn
|
6
|
+
|
7
|
+
def self.default_query_options
|
8
|
+
@default_query_options ||= {
|
9
|
+
auto_commit: true
|
10
|
+
}
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize(opts = {})
|
14
|
+
raise Cubrid2::Error, 'Options parameter must be a Hash' unless opts.is_a? Hash
|
15
|
+
|
16
|
+
opts = Cubrid2::Util.key_hash_as_symbols(opts)
|
17
|
+
@read_timeout = nil
|
18
|
+
@query_options = self.class.default_query_options.dup
|
19
|
+
@query_options.merge! opts
|
20
|
+
|
21
|
+
%i[auto_commit].each do |key|
|
22
|
+
next unless opts.key?(key)
|
23
|
+
|
24
|
+
case key
|
25
|
+
when :auto_commit
|
26
|
+
send(:"#{key}=", !!opts[key]) # rubocop:disable Style/DoubleNegation
|
27
|
+
else
|
28
|
+
send(:"#{key}=", opts[key])
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
flags = 0
|
33
|
+
|
34
|
+
user = opts[:username] || opts[:user]
|
35
|
+
pass = opts[:password] || opts[:pass]
|
36
|
+
host = opts[:host] || opts[:hostname]
|
37
|
+
port = opts[:port]
|
38
|
+
database = opts[:database] || opts[:dbname] || opts[:db]
|
39
|
+
|
40
|
+
# Correct the data types before passing these values down to the C level
|
41
|
+
user = user.to_s unless user.nil?
|
42
|
+
pass = pass.to_s unless pass.nil?
|
43
|
+
host = host.to_s unless host.nil?
|
44
|
+
port = port.to_i unless port.nil?
|
45
|
+
database = database.to_s unless database.nil?
|
46
|
+
|
47
|
+
@conn = Cubrid.connect database, host, port, user, pass
|
48
|
+
end
|
49
|
+
|
50
|
+
def query(sql, options = {})
|
51
|
+
Thread.handle_interrupt(::Cubrid2::Util::TIMEOUT_ERROR_CLASS => :never) do
|
52
|
+
_query(sql, @query_options.merge(options))
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def _query(sql, _options)
|
57
|
+
@conn.query(sql)
|
58
|
+
end
|
59
|
+
|
60
|
+
def query_info
|
61
|
+
info = query_info_string
|
62
|
+
return {} unless info
|
63
|
+
|
64
|
+
info_hash = {}
|
65
|
+
info.split.each_slice(2) { |s| info_hash[s[0].downcase.delete(':').to_sym] = s[1].to_i }
|
66
|
+
info_hash
|
67
|
+
end
|
68
|
+
|
69
|
+
def info
|
70
|
+
self.class.info
|
71
|
+
end
|
72
|
+
|
73
|
+
def ping
|
74
|
+
@conn.server_version.present?
|
75
|
+
end
|
76
|
+
|
77
|
+
def server_version
|
78
|
+
@conn.server_version
|
79
|
+
end
|
80
|
+
|
81
|
+
def close
|
82
|
+
@conn.close
|
83
|
+
end
|
84
|
+
|
85
|
+
def auto_commit
|
86
|
+
@conn.auto_commit
|
87
|
+
end
|
88
|
+
|
89
|
+
def auto_commit=(flag)
|
90
|
+
@conn.auto_commit = flag
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|