ovirt_metrics 2.0.0 → 3.0.3

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.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +0 -1
  3. data/lib/active_record/connection_adapters/ovirt_postgresql_adapter.rb +31 -0
  4. data/lib/ovirt_metrics/version.rb +1 -1
  5. data/lib/ovirt_metrics.rb +4 -8
  6. metadata +32 -57
  7. data/lib/active_record/connection_adapters/ovirt_legacy_postgresql/column.rb +0 -15
  8. data/lib/active_record/connection_adapters/ovirt_legacy_postgresql/database_statements.rb +0 -170
  9. data/lib/active_record/connection_adapters/ovirt_legacy_postgresql/explain_pretty_printer.rb +0 -42
  10. data/lib/active_record/connection_adapters/ovirt_legacy_postgresql/oid/array.rb +0 -70
  11. data/lib/active_record/connection_adapters/ovirt_legacy_postgresql/oid/bit.rb +0 -52
  12. data/lib/active_record/connection_adapters/ovirt_legacy_postgresql/oid/bit_varying.rb +0 -13
  13. data/lib/active_record/connection_adapters/ovirt_legacy_postgresql/oid/bytea.rb +0 -15
  14. data/lib/active_record/connection_adapters/ovirt_legacy_postgresql/oid/cidr.rb +0 -48
  15. data/lib/active_record/connection_adapters/ovirt_legacy_postgresql/oid/date_time.rb +0 -21
  16. data/lib/active_record/connection_adapters/ovirt_legacy_postgresql/oid/decimal.rb +0 -13
  17. data/lib/active_record/connection_adapters/ovirt_legacy_postgresql/oid/enum.rb +0 -19
  18. data/lib/active_record/connection_adapters/ovirt_legacy_postgresql/oid/hstore.rb +0 -59
  19. data/lib/active_record/connection_adapters/ovirt_legacy_postgresql/oid/inet.rb +0 -13
  20. data/lib/active_record/connection_adapters/ovirt_legacy_postgresql/oid/json.rb +0 -10
  21. data/lib/active_record/connection_adapters/ovirt_legacy_postgresql/oid/jsonb.rb +0 -23
  22. data/lib/active_record/connection_adapters/ovirt_legacy_postgresql/oid/money.rb +0 -39
  23. data/lib/active_record/connection_adapters/ovirt_legacy_postgresql/oid/point.rb +0 -43
  24. data/lib/active_record/connection_adapters/ovirt_legacy_postgresql/oid/rails_5_1_point.rb +0 -50
  25. data/lib/active_record/connection_adapters/ovirt_legacy_postgresql/oid/range.rb +0 -93
  26. data/lib/active_record/connection_adapters/ovirt_legacy_postgresql/oid/specialized_string.rb +0 -15
  27. data/lib/active_record/connection_adapters/ovirt_legacy_postgresql/oid/type_map_initializer.rb +0 -109
  28. data/lib/active_record/connection_adapters/ovirt_legacy_postgresql/oid/uuid.rb +0 -21
  29. data/lib/active_record/connection_adapters/ovirt_legacy_postgresql/oid/vector.rb +0 -26
  30. data/lib/active_record/connection_adapters/ovirt_legacy_postgresql/oid/xml.rb +0 -28
  31. data/lib/active_record/connection_adapters/ovirt_legacy_postgresql/oid.rb +0 -31
  32. data/lib/active_record/connection_adapters/ovirt_legacy_postgresql/quoting.rb +0 -116
  33. data/lib/active_record/connection_adapters/ovirt_legacy_postgresql/referential_integrity.rb +0 -49
  34. data/lib/active_record/connection_adapters/ovirt_legacy_postgresql/schema_definitions.rb +0 -180
  35. data/lib/active_record/connection_adapters/ovirt_legacy_postgresql/schema_dumper.rb +0 -47
  36. data/lib/active_record/connection_adapters/ovirt_legacy_postgresql/schema_statements.rb +0 -682
  37. data/lib/active_record/connection_adapters/ovirt_legacy_postgresql/type_metadata.rb +0 -35
  38. data/lib/active_record/connection_adapters/ovirt_legacy_postgresql/utils.rb +0 -77
  39. data/lib/active_record/connection_adapters/ovirt_legacy_postgresql_adapter.rb +0 -856
@@ -1,856 +0,0 @@
1
- # Rails 5 supports PostgreSQL >= 9.1
2
- # This is a custom Postgres adapter for ovirt_metrics to utilize
3
- # ActiveRecord 5.x while connecting to a PostgreSQL 8.4 database.
4
- # It's also a necessary evil, so ye be warned.
5
-
6
- # Make sure we're using pg high enough for type casts and Ruby 2.2+ compatibility
7
- gem 'pg', '~> 0.18'
8
- require 'pg'
9
-
10
- require "active_record/connection_adapters/abstract_adapter"
11
- require "active_record/connection_adapters/ovirt_legacy_postgresql/column"
12
- require "active_record/connection_adapters/ovirt_legacy_postgresql/database_statements"
13
- require "active_record/connection_adapters/ovirt_legacy_postgresql/explain_pretty_printer"
14
- require "active_record/connection_adapters/ovirt_legacy_postgresql/oid"
15
- require "active_record/connection_adapters/ovirt_legacy_postgresql/quoting"
16
- require "active_record/connection_adapters/ovirt_legacy_postgresql/referential_integrity"
17
- require "active_record/connection_adapters/ovirt_legacy_postgresql/schema_definitions"
18
- require "active_record/connection_adapters/ovirt_legacy_postgresql/schema_dumper"
19
- require "active_record/connection_adapters/ovirt_legacy_postgresql/schema_statements"
20
- require "active_record/connection_adapters/ovirt_legacy_postgresql/type_metadata"
21
- require "active_record/connection_adapters/ovirt_legacy_postgresql/utils"
22
- require "active_record/connection_adapters/statement_pool"
23
-
24
- module ActiveRecord
25
- module ConnectionHandling # :nodoc:
26
- # Establishes a connection to the database that's used by all Active Record objects
27
- def ovirt_legacy_postgresql_connection(config)
28
- conn_params = config.symbolize_keys
29
-
30
- conn_params.delete_if { |_, v| v.nil? }
31
-
32
- # Map ActiveRecords param names to PGs.
33
- conn_params[:user] = conn_params.delete(:username) if conn_params[:username]
34
- conn_params[:dbname] = conn_params.delete(:database) if conn_params[:database]
35
-
36
- # Forward only valid config params to PGconn.connect.
37
- valid_conn_param_keys = PGconn.conndefaults_hash.keys + [:requiressl]
38
- conn_params.slice!(*valid_conn_param_keys)
39
-
40
- # The postgres drivers don't allow the creation of an unconnected PGconn object,
41
- # so just pass a nil connection object for the time being.
42
- ConnectionAdapters::OvirtLegacyPostgreSQLAdapter.new(nil, logger, conn_params, config)
43
- end
44
- end
45
-
46
- module ConnectionAdapters
47
- # The OvirtLegacyPostgreSQL adapter works with the native C (https://bitbucket.org/ged/ruby-pg) driver.
48
- #
49
- # Options:
50
- #
51
- # * <tt>:host</tt> - Defaults to a Unix-domain socket in /tmp. On machines without Unix-domain sockets,
52
- # the default is to connect to localhost.
53
- # * <tt>:port</tt> - Defaults to 5432.
54
- # * <tt>:username</tt> - Defaults to be the same as the operating system name of the user running the application.
55
- # * <tt>:password</tt> - Password to be used if the server demands password authentication.
56
- # * <tt>:database</tt> - Defaults to be the same as the user name.
57
- # * <tt>:schema_search_path</tt> - An optional schema search path for the connection given
58
- # as a string of comma-separated schema names. This is backward-compatible with the <tt>:schema_order</tt> option.
59
- # * <tt>:encoding</tt> - An optional client encoding that is used in a <tt>SET client_encoding TO
60
- # <encoding></tt> call on the connection.
61
- # * <tt>:min_messages</tt> - An optional client min messages that is used in a
62
- # <tt>SET client_min_messages TO <min_messages></tt> call on the connection.
63
- # * <tt>:variables</tt> - An optional hash of additional parameters that
64
- # will be used in <tt>SET SESSION key = val</tt> calls on the connection.
65
- # * <tt>:insert_returning</tt> - An optional boolean to control the use of <tt>RETURNING</tt> for <tt>INSERT</tt> statements
66
- # defaults to true.
67
- #
68
- # Any further options are used as connection parameters to libpq. See
69
- # http://www.postgresql.org/docs/current/static/libpq-connect.html for the
70
- # list of parameters.
71
- #
72
- # In addition, default connection parameters of libpq can be set per environment variables.
73
- # See http://www.postgresql.org/docs/current/static/libpq-envars.html .
74
- class OvirtLegacyPostgreSQLAdapter < AbstractAdapter
75
- ADAPTER_NAME = 'OvirtLegacyPostgreSQL'.freeze
76
-
77
- NATIVE_DATABASE_TYPES = {
78
- primary_key: "serial primary key",
79
- string: { name: "character varying" },
80
- text: { name: "text" },
81
- integer: { name: "integer" },
82
- float: { name: "float" },
83
- decimal: { name: "decimal" },
84
- datetime: { name: "timestamp" },
85
- time: { name: "time" },
86
- date: { name: "date" },
87
- daterange: { name: "daterange" },
88
- numrange: { name: "numrange" },
89
- tsrange: { name: "tsrange" },
90
- tstzrange: { name: "tstzrange" },
91
- int4range: { name: "int4range" },
92
- int8range: { name: "int8range" },
93
- binary: { name: "bytea" },
94
- boolean: { name: "boolean" },
95
- xml: { name: "xml" },
96
- tsvector: { name: "tsvector" },
97
- hstore: { name: "hstore" },
98
- inet: { name: "inet" },
99
- cidr: { name: "cidr" },
100
- macaddr: { name: "macaddr" },
101
- uuid: { name: "uuid" },
102
- json: { name: "json" },
103
- jsonb: { name: "jsonb" },
104
- ltree: { name: "ltree" },
105
- citext: { name: "citext" },
106
- point: { name: "point" },
107
- line: { name: "line" },
108
- lseg: { name: "lseg" },
109
- box: { name: "box" },
110
- path: { name: "path" },
111
- polygon: { name: "polygon" },
112
- circle: { name: "circle" },
113
- bit: { name: "bit" },
114
- bit_varying: { name: "bit varying" },
115
- money: { name: "money" },
116
- }
117
-
118
- OID = OvirtLegacyPostgreSQL::OID #:nodoc:
119
-
120
- include OvirtLegacyPostgreSQL::Quoting
121
- include OvirtLegacyPostgreSQL::ReferentialIntegrity
122
- include OvirtLegacyPostgreSQL::SchemaStatements
123
- include OvirtLegacyPostgreSQL::DatabaseStatements
124
- include OvirtLegacyPostgreSQL::ColumnDumper
125
-
126
- def schema_creation # :nodoc:
127
- OvirtLegacyPostgreSQL::SchemaCreation.new self
128
- end
129
-
130
- def arel_visitor # :nodoc:
131
- Arel::Visitors::PostgreSQL.new(self)
132
- end
133
-
134
- # Returns true, since this connection adapter supports prepared statement
135
- # caching.
136
- def supports_statement_cache?
137
- true
138
- end
139
-
140
- def supports_index_sort_order?
141
- true
142
- end
143
-
144
- def supports_partial_index?
145
- true
146
- end
147
-
148
- def supports_expression_index?
149
- true
150
- end
151
-
152
- def supports_transaction_isolation?
153
- true
154
- end
155
-
156
- def supports_foreign_keys?
157
- true
158
- end
159
-
160
- def supports_views?
161
- true
162
- end
163
-
164
- def supports_datetime_with_precision?
165
- true
166
- end
167
-
168
- def supports_json?
169
- postgresql_version >= 90200
170
- end
171
-
172
- def supports_comments?
173
- true
174
- end
175
-
176
- def supports_comments_in_create?
177
- false
178
- end
179
-
180
- def supports_savepoints?
181
- true
182
- end
183
-
184
- def supports_collation?
185
- postgresql_version >= 90100
186
- end
187
-
188
- def index_algorithms
189
- { concurrently: 'CONCURRENTLY' }
190
- end
191
-
192
- class StatementPool < ConnectionAdapters::StatementPool
193
- def initialize(connection, max)
194
- super(max)
195
- @connection = connection
196
- @counter = 0
197
- end
198
-
199
- def next_key
200
- "a#{@counter + 1}"
201
- end
202
-
203
- def []=(sql, key)
204
- super.tap { @counter += 1 }
205
- end
206
-
207
- private
208
-
209
- def dealloc(key)
210
- @connection.query "DEALLOCATE #{key}" if connection_active?
211
- end
212
-
213
- def connection_active?
214
- @connection.status == PGconn::CONNECTION_OK
215
- rescue PGError
216
- false
217
- end
218
- end
219
-
220
- # Initializes and connects a OvirtLegacyPostgreSQL adapter.
221
- def initialize(connection, logger, connection_parameters, config)
222
- super(connection, logger, config)
223
-
224
- @connection_parameters = connection_parameters
225
-
226
- # @local_tz is initialized as nil to avoid warnings when connect tries to use it
227
- @local_tz = nil
228
- @table_alias_length = nil
229
-
230
- connect
231
- add_pg_encoders
232
- @statements = StatementPool.new @connection,
233
- self.class.type_cast_config_to_integer(config[:statement_limit])
234
-
235
- if postgresql_version < 80200
236
- raise "Your version of PostgreSQL (#{postgresql_version}) is too old, please upgrade!"
237
- end
238
-
239
- add_pg_decoders
240
-
241
- @type_map = Type::HashLookupTypeMap.new
242
- initialize_type_map(type_map)
243
- @local_tz = execute('SHOW TIME ZONE', 'SCHEMA').first["TimeZone"]
244
- @use_insert_returning = @config.key?(:insert_returning) ? self.class.type_cast_config_to_boolean(@config[:insert_returning]) : true
245
- end
246
-
247
- # Clears the prepared statements cache.
248
- def clear_cache!
249
- @statements.clear
250
- end
251
-
252
- def truncate(table_name, name = nil)
253
- exec_query "TRUNCATE TABLE #{quote_table_name(table_name)}", name, []
254
- end
255
-
256
- # Is this connection alive and ready for queries?
257
- def active?
258
- @connection.query 'SELECT 1'
259
- true
260
- rescue PGError
261
- false
262
- end
263
-
264
- # Close then reopen the connection.
265
- def reconnect!
266
- super
267
- @connection.reset
268
- configure_connection
269
- end
270
-
271
- def reset!
272
- clear_cache!
273
- reset_transaction
274
- unless @connection.transaction_status == ::PG::PQTRANS_IDLE
275
- @connection.query 'ROLLBACK'
276
- end
277
- @connection.query 'DISCARD ALL'
278
- configure_connection
279
- end
280
-
281
- # Disconnects from the database if already connected. Otherwise, this
282
- # method does nothing.
283
- def disconnect!
284
- super
285
- @connection.close rescue nil
286
- end
287
-
288
- def native_database_types #:nodoc:
289
- NATIVE_DATABASE_TYPES
290
- end
291
-
292
- # Returns true, since this connection adapter supports migrations.
293
- def supports_migrations?
294
- true
295
- end
296
-
297
- # Does PostgreSQL support finding primary key on non-Active Record tables?
298
- def supports_primary_key? #:nodoc:
299
- true
300
- end
301
-
302
- def set_standard_conforming_strings
303
- execute('SET standard_conforming_strings = on', 'SCHEMA')
304
- end
305
-
306
- def supports_ddl_transactions?
307
- true
308
- end
309
-
310
- def supports_advisory_locks?
311
- true
312
- end
313
-
314
- def supports_explain?
315
- true
316
- end
317
-
318
- def supports_extensions?
319
- postgresql_version >= 90100
320
- end
321
-
322
- # Range datatypes weren't introduced until PostgreSQL 9.2
323
- def supports_ranges?
324
- postgresql_version >= 90200
325
- end
326
-
327
- def supports_materialized_views?
328
- postgresql_version >= 90300
329
- end
330
-
331
- def get_advisory_lock(lock_id) # :nodoc:
332
- unless lock_id.is_a?(Integer) && lock_id.bit_length <= 63
333
- raise(ArgumentError, "Postgres requires advisory lock ids to be a signed 64 bit integer")
334
- end
335
- select_value("SELECT pg_try_advisory_lock(#{lock_id});")
336
- end
337
-
338
- def release_advisory_lock(lock_id) # :nodoc:
339
- unless lock_id.is_a?(Integer) && lock_id.bit_length <= 63
340
- raise(ArgumentError, "Postgres requires advisory lock ids to be a signed 64 bit integer")
341
- end
342
- select_value("SELECT pg_advisory_unlock(#{lock_id})")
343
- end
344
-
345
- def enable_extension(name)
346
- exec_query("CREATE EXTENSION IF NOT EXISTS \"#{name}\"").tap {
347
- reload_type_map
348
- }
349
- end
350
-
351
- def disable_extension(name)
352
- exec_query("DROP EXTENSION IF EXISTS \"#{name}\" CASCADE").tap {
353
- reload_type_map
354
- }
355
- end
356
-
357
- def extension_enabled?(name)
358
- if supports_extensions?
359
- res = exec_query "SELECT EXISTS(SELECT * FROM pg_available_extensions WHERE name = '#{name}' AND installed_version IS NOT NULL) as enabled",
360
- 'SCHEMA'
361
- res.cast_values.first
362
- end
363
- end
364
-
365
- def extensions
366
- if supports_extensions?
367
- exec_query("SELECT extname from pg_extension", "SCHEMA").cast_values
368
- else
369
- super
370
- end
371
- end
372
-
373
- # Returns the configured supported identifier length supported by PostgreSQL
374
- def table_alias_length
375
- @table_alias_length ||= query('SHOW max_identifier_length', 'SCHEMA')[0][0].to_i
376
- end
377
-
378
- # Set the authorized user for this session
379
- def session_auth=(user)
380
- clear_cache!
381
- exec_query "SET SESSION AUTHORIZATION #{user}"
382
- end
383
-
384
- def use_insert_returning?
385
- @use_insert_returning
386
- end
387
-
388
- def valid_type?(type)
389
- !native_database_types[type].nil?
390
- end
391
-
392
- def update_table_definition(table_name, base) #:nodoc:
393
- OvirtLegacyPostgreSQL::Table.new(table_name, base)
394
- end
395
-
396
- def lookup_cast_type(sql_type) # :nodoc:
397
- oid = execute("SELECT #{quote(sql_type)}::regtype::oid", "SCHEMA").first['oid'].to_i
398
- super(oid)
399
- end
400
-
401
- def column_name_for_operation(operation, node) # :nodoc:
402
- OPERATION_ALIASES.fetch(operation) { operation.downcase }
403
- end
404
-
405
- OPERATION_ALIASES = { # :nodoc:
406
- "maximum" => "max",
407
- "minimum" => "min",
408
- "average" => "avg",
409
- }
410
-
411
- # Returns the version of the connected PostgreSQL server.
412
- def postgresql_version
413
- @connection.server_version
414
- end
415
-
416
- protected
417
-
418
- # See http://www.postgresql.org/docs/current/static/errcodes-appendix.html
419
- VALUE_LIMIT_VIOLATION = "22001"
420
- FOREIGN_KEY_VIOLATION = "23503"
421
- UNIQUE_VIOLATION = "23505"
422
-
423
- def translate_exception(exception, message)
424
- return exception unless exception.respond_to?(:result)
425
-
426
- case exception.result.try(:error_field, PGresult::PG_DIAG_SQLSTATE)
427
- when UNIQUE_VIOLATION
428
- RecordNotUnique.new(message)
429
- when FOREIGN_KEY_VIOLATION
430
- InvalidForeignKey.new(message)
431
- when VALUE_LIMIT_VIOLATION
432
- ValueTooLong.new(message)
433
- else
434
- super
435
- end
436
- end
437
-
438
- private
439
-
440
- def get_oid_type(oid, fmod, column_name, sql_type = '') # :nodoc:
441
- if !type_map.key?(oid)
442
- load_additional_types(type_map, [oid])
443
- end
444
-
445
- type_map.fetch(oid, fmod, sql_type) {
446
- warn "unknown OID #{oid}: failed to recognize type of '#{column_name}'. It will be treated as String."
447
- Type::Value.new.tap do |cast_type|
448
- type_map.register_type(oid, cast_type)
449
- end
450
- }
451
- end
452
-
453
- def initialize_type_map(m) # :nodoc:
454
- register_class_with_limit m, 'int2', Type::Integer
455
- register_class_with_limit m, 'int4', Type::Integer
456
- register_class_with_limit m, 'int8', Type::Integer
457
- m.alias_type 'oid', 'int2'
458
- m.register_type 'float4', Type::Float.new
459
- m.alias_type 'float8', 'float4'
460
- m.register_type 'text', Type::Text.new
461
- register_class_with_limit m, 'varchar', Type::String
462
- m.alias_type 'char', 'varchar'
463
- m.alias_type 'name', 'varchar'
464
- m.alias_type 'bpchar', 'varchar'
465
- m.register_type 'bool', Type::Boolean.new
466
- register_class_with_limit m, 'bit', OID::Bit
467
- register_class_with_limit m, 'varbit', OID::BitVarying
468
- m.alias_type 'timestamptz', 'timestamp'
469
- m.register_type 'date', Type::Date.new
470
-
471
- m.register_type 'money', OID::Money.new
472
- m.register_type 'bytea', OID::Bytea.new
473
- m.register_type 'point', OID::Point.new
474
- m.register_type 'hstore', OID::Hstore.new
475
- m.register_type 'json', OID::Json.new
476
- m.register_type 'jsonb', OID::Jsonb.new
477
- m.register_type 'cidr', OID::Cidr.new
478
- m.register_type 'inet', OID::Inet.new
479
- m.register_type 'uuid', OID::Uuid.new
480
- m.register_type 'xml', OID::Xml.new
481
- m.register_type 'tsvector', OID::SpecializedString.new(:tsvector)
482
- m.register_type 'macaddr', OID::SpecializedString.new(:macaddr)
483
- m.register_type 'citext', OID::SpecializedString.new(:citext)
484
- m.register_type 'ltree', OID::SpecializedString.new(:ltree)
485
- m.register_type 'line', OID::SpecializedString.new(:line)
486
- m.register_type 'lseg', OID::SpecializedString.new(:lseg)
487
- m.register_type 'box', OID::SpecializedString.new(:box)
488
- m.register_type 'path', OID::SpecializedString.new(:path)
489
- m.register_type 'polygon', OID::SpecializedString.new(:polygon)
490
- m.register_type 'circle', OID::SpecializedString.new(:circle)
491
-
492
- # FIXME: why are we keeping these types as strings?
493
- m.alias_type 'interval', 'varchar'
494
-
495
- register_class_with_precision m, 'time', Type::Time
496
- register_class_with_precision m, 'timestamp', OID::DateTime
497
-
498
- m.register_type 'numeric' do |_, fmod, sql_type|
499
- precision = extract_precision(sql_type)
500
- scale = extract_scale(sql_type)
501
-
502
- # The type for the numeric depends on the width of the field,
503
- # so we'll do something special here.
504
- #
505
- # When dealing with decimal columns:
506
- #
507
- # places after decimal = fmod - 4 & 0xffff
508
- # places before decimal = (fmod - 4) >> 16 & 0xffff
509
- if fmod && (fmod - 4 & 0xffff).zero?
510
- # FIXME: Remove this class, and the second argument to
511
- # lookups on PG
512
- Type::DecimalWithoutScale.new(precision: precision)
513
- else
514
- OID::Decimal.new(precision: precision, scale: scale)
515
- end
516
- end
517
-
518
- load_additional_types(m)
519
- end
520
-
521
- def extract_limit(sql_type) # :nodoc:
522
- case sql_type
523
- when /^bigint/i, /^int8/i
524
- 8
525
- when /^smallint/i
526
- 2
527
- else
528
- super
529
- end
530
- end
531
-
532
- # Extracts the value from a PostgreSQL column default definition.
533
- def extract_value_from_default(default) # :nodoc:
534
- case default
535
- # Quoted types
536
- when /\A[\(B]?'(.*)'.*::"?([\w. ]+)"?(?:\[\])?\z/m
537
- # The default 'now'::date is CURRENT_DATE
538
- if $1 == "now".freeze && $2 == "date".freeze
539
- nil
540
- else
541
- $1.gsub("''".freeze, "'".freeze)
542
- end
543
- # Boolean types
544
- when 'true'.freeze, 'false'.freeze
545
- default
546
- # Numeric types
547
- when /\A\(?(-?\d+(\.\d*)?)\)?(::bigint)?\z/
548
- $1
549
- # Object identifier types
550
- when /\A-?\d+\z/
551
- $1
552
- else
553
- # Anything else is blank, some user type, or some function
554
- # and we can't know the value of that, so return nil.
555
- nil
556
- end
557
- end
558
-
559
- def extract_default_function(default_value, default) # :nodoc:
560
- default if has_default_function?(default_value, default)
561
- end
562
-
563
- def has_default_function?(default_value, default) # :nodoc:
564
- !default_value && (%r{\w+\(.*\)|\(.*\)::\w+} === default)
565
- end
566
-
567
- def load_additional_types(type_map, oids = nil) # :nodoc:
568
- initializer = OID::TypeMapInitializer.new(type_map)
569
-
570
- if supports_ranges?
571
- query = <<-SQL
572
- SELECT t.oid, t.typname, t.typelem, t.typdelim, t.typinput, r.rngsubtype, t.typtype, t.typbasetype
573
- FROM pg_type as t
574
- LEFT JOIN pg_range as r ON oid = rngtypid
575
- SQL
576
- else
577
- query = <<-SQL
578
- SELECT t.oid, t.typname, t.typelem, t.typdelim, t.typinput, t.typtype, t.typbasetype
579
- FROM pg_type as t
580
- SQL
581
- end
582
-
583
- if oids
584
- query += "WHERE t.oid::integer IN (%s)" % oids.join(", ")
585
- else
586
- query += initializer.query_conditions_for_initial_load(type_map)
587
- end
588
-
589
- execute_and_clear(query, 'SCHEMA', []) do |records|
590
- initializer.run(records)
591
- end
592
- end
593
-
594
- FEATURE_NOT_SUPPORTED = "0A000" #:nodoc:
595
-
596
- def execute_and_clear(sql, name, binds, prepare: false)
597
- if without_prepared_statement?(binds)
598
- result = exec_no_cache(sql, name, [])
599
- elsif !prepare
600
- result = exec_no_cache(sql, name, binds)
601
- else
602
- result = exec_cache(sql, name, binds)
603
- end
604
- ret = yield result
605
- result.clear
606
- ret
607
- end
608
-
609
- def exec_no_cache(sql, name, binds)
610
- type_casted_binds = binds.map { |attr| type_cast(attr.value_for_database) }
611
- log(sql, name, binds) { @connection.async_exec(sql, type_casted_binds) }
612
- end
613
-
614
- def exec_cache(sql, name, binds)
615
- stmt_key = prepare_statement(sql)
616
- type_casted_binds = binds.map { |attr| type_cast(attr.value_for_database) }
617
-
618
- log(sql, name, binds, stmt_key) do
619
- @connection.exec_prepared(stmt_key, type_casted_binds)
620
- end
621
- rescue ActiveRecord::StatementInvalid => e
622
- raise unless is_cached_plan_failure?(e)
623
-
624
- # Nothing we can do if we are in a transaction because all commands
625
- # will raise InFailedSQLTransaction
626
- if in_transaction?
627
- raise ActiveRecord::PreparedStatementCacheExpired.new(e.cause.message)
628
- else
629
- # outside of transactions we can simply flush this query and retry
630
- @statements.delete sql_key(sql)
631
- retry
632
- end
633
- end
634
-
635
- # Annoyingly, the code for prepared statements whose return value may
636
- # have changed is FEATURE_NOT_SUPPORTED.
637
- #
638
- # This covers various different error types so we need to do additional
639
- # work to classify the exception definitively as a
640
- # ActiveRecord::PreparedStatementCacheExpired
641
- #
642
- # Check here for more details:
643
- # http://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/backend/utils/cache/plancache.c#l573
644
- CACHED_PLAN_HEURISTIC = 'cached plan must not change result type'.freeze
645
- def is_cached_plan_failure?(e)
646
- pgerror = e.cause
647
- code = pgerror.result.result_error_field(PGresult::PG_DIAG_SQLSTATE)
648
- code == FEATURE_NOT_SUPPORTED && pgerror.message.include?(CACHED_PLAN_HEURISTIC)
649
- rescue
650
- false
651
- end
652
-
653
- def in_transaction?
654
- open_transactions > 0
655
- end
656
-
657
- # Returns the statement identifier for the client side cache
658
- # of statements
659
- def sql_key(sql)
660
- "#{schema_search_path}-#{sql}"
661
- end
662
-
663
- # Prepare the statement if it hasn't been prepared, return
664
- # the statement key.
665
- def prepare_statement(sql)
666
- sql_key = sql_key(sql)
667
- unless @statements.key? sql_key
668
- nextkey = @statements.next_key
669
- begin
670
- @connection.prepare nextkey, sql
671
- rescue => e
672
- raise translate_exception_class(e, sql)
673
- end
674
- # Clear the queue
675
- @connection.get_last_result
676
- @statements[sql_key] = nextkey
677
- end
678
- @statements[sql_key]
679
- end
680
-
681
- # Connects to a PostgreSQL server and sets up the adapter depending on the
682
- # connected server's characteristics.
683
- def connect
684
- @connection = PGconn.connect(@connection_parameters)
685
- configure_connection
686
- rescue ::PG::Error => error
687
- if error.message.include?("does not exist")
688
- raise ActiveRecord::NoDatabaseError
689
- else
690
- raise
691
- end
692
- end
693
-
694
- # Configures the encoding, verbosity, schema search path, and time zone of the connection.
695
- # This is called by #connect and should not be called manually.
696
- def configure_connection
697
- if @config[:encoding]
698
- @connection.set_client_encoding(@config[:encoding])
699
- end
700
- self.client_min_messages = @config[:min_messages] || 'warning'
701
- self.schema_search_path = @config[:schema_search_path] || @config[:schema_order]
702
-
703
- # Use standard-conforming strings so we don't have to do the E'...' dance.
704
- set_standard_conforming_strings
705
-
706
- # If using Active Record's time zone support configure the connection to return
707
- # TIMESTAMP WITH ZONE types in UTC.
708
- # (SET TIME ZONE does not use an equals sign like other SET variables)
709
- if ActiveRecord::Base.default_timezone == :utc
710
- execute("SET time zone 'UTC'", 'SCHEMA')
711
- elsif @local_tz
712
- execute("SET time zone '#{@local_tz}'", 'SCHEMA')
713
- end
714
-
715
- # SET statements from :variables config hash
716
- # http://www.postgresql.org/docs/current/static/sql-set.html
717
- variables = @config[:variables] || {}
718
- variables.map do |k, v|
719
- if v == ':default' || v == :default
720
- # Sets the value to the global or compile default
721
- execute("SET SESSION #{k} TO DEFAULT", 'SCHEMA')
722
- elsif !v.nil?
723
- execute("SET SESSION #{k} TO #{quote(v)}", 'SCHEMA')
724
- end
725
- end
726
- end
727
-
728
- # Returns the current ID of a table's sequence.
729
- def last_insert_id_result(sequence_name) # :nodoc:
730
- exec_query("SELECT currval('#{sequence_name}')", 'SQL')
731
- end
732
-
733
- # Returns the list of a table's column names, data types, and default values.
734
- #
735
- # The underlying query is roughly:
736
- # SELECT column.name, column.type, default.value, column.comment
737
- # FROM column LEFT JOIN default
738
- # ON column.table_id = default.table_id
739
- # AND column.num = default.column_num
740
- # WHERE column.table_id = get_table_id('table_name')
741
- # AND column.num > 0
742
- # AND NOT column.is_dropped
743
- # ORDER BY column.num
744
- #
745
- # If the table name is not prefixed with a schema, the database will
746
- # take the first match from the schema search path.
747
- #
748
- # Query implementation notes:
749
- # - format_type includes the column size constraint, e.g. varchar(50)
750
- # - ::regclass is a function that gives the id for a table name
751
- def column_definitions(table_name) # :nodoc:
752
- query(<<-end_sql, 'SCHEMA')
753
- SELECT a.attname, format_type(a.atttypid, a.atttypmod),
754
- pg_get_expr(d.adbin, d.adrelid), a.attnotnull, a.atttypid, a.atttypmod
755
- #{', (SELECT c.collname FROM pg_collation c, pg_type t
756
- WHERE c.oid = a.attcollation AND t.oid = a.atttypid
757
- AND a.attcollation <> t.typcollation)' if supports_collation?}
758
- , col_description(a.attrelid, a.attnum) AS comment
759
- FROM pg_attribute a LEFT JOIN pg_attrdef d
760
- ON a.attrelid = d.adrelid AND a.attnum = d.adnum
761
- WHERE a.attrelid = '#{quote_table_name(table_name)}'::regclass
762
- AND a.attnum > 0 AND NOT a.attisdropped
763
- ORDER BY a.attnum
764
- end_sql
765
- end
766
-
767
- def extract_table_ref_from_insert_sql(sql) # :nodoc:
768
- sql[/into\s("[A-Za-z0-9_."\[\]\s]+"|[A-Za-z0-9_."\[\]]+)\s*/im]
769
- $1.strip if $1
770
- end
771
-
772
- def create_table_definition(*args) # :nodoc:
773
- OvirtLegacyPostgreSQL::TableDefinition.new(*args)
774
- end
775
-
776
- def can_perform_case_insensitive_comparison_for?(column)
777
- @case_insensitive_cache ||= {}
778
- @case_insensitive_cache[column.sql_type] ||= begin
779
- sql = <<-end_sql
780
- SELECT exists(
781
- SELECT * FROM pg_proc
782
- INNER JOIN pg_cast
783
- ON casttarget::text::oidvector = proargtypes
784
- WHERE proname = 'lower'
785
- AND castsource = '#{column.sql_type}'::regtype::oid
786
- )
787
- end_sql
788
- execute_and_clear(sql, "SCHEMA", []) do |result|
789
- result.getvalue(0, 0)
790
- end
791
- end
792
- end
793
-
794
- def add_pg_encoders
795
- map = PG::TypeMapByClass.new
796
- map[Integer] = PG::TextEncoder::Integer.new
797
- map[TrueClass] = PG::TextEncoder::Boolean.new
798
- map[FalseClass] = PG::TextEncoder::Boolean.new
799
- map[Float] = PG::TextEncoder::Float.new
800
- @connection.type_map_for_queries = map
801
- end
802
-
803
- def add_pg_decoders
804
- coders_by_name = {
805
- 'int2' => PG::TextDecoder::Integer,
806
- 'int4' => PG::TextDecoder::Integer,
807
- 'int8' => PG::TextDecoder::Integer,
808
- 'oid' => PG::TextDecoder::Integer,
809
- 'float4' => PG::TextDecoder::Float,
810
- 'float8' => PG::TextDecoder::Float,
811
- 'bool' => PG::TextDecoder::Boolean,
812
- }
813
- known_coder_types = coders_by_name.keys.map { |n| quote(n) }
814
- query = <<-SQL % known_coder_types.join(", ")
815
- SELECT t.oid, t.typname
816
- FROM pg_type as t
817
- WHERE t.typname IN (%s)
818
- SQL
819
- coders = execute_and_clear(query, "SCHEMA", []) do |result|
820
- result
821
- .map { |row| construct_coder(row, coders_by_name[row['typname']]) }
822
- .compact
823
- end
824
-
825
- map = PG::TypeMapByOid.new
826
- coders.each { |coder| map.add_coder(coder) }
827
- @connection.type_map_for_results = map
828
- end
829
-
830
- def construct_coder(row, coder_class)
831
- return unless coder_class
832
- coder_class.new(oid: row['oid'].to_i, name: row['typname'])
833
- end
834
-
835
- ActiveRecord::Type.add_modifier({ array: true }, OID::Array, adapter: :ovirt_legacy_postgresql)
836
- ActiveRecord::Type.add_modifier({ range: true }, OID::Range, adapter: :ovirt_legacy_postgresql)
837
- ActiveRecord::Type.register(:bit, OID::Bit, adapter: :ovirt_legacy_postgresql)
838
- ActiveRecord::Type.register(:bit_varying, OID::BitVarying, adapter: :ovirt_legacy_postgresql)
839
- ActiveRecord::Type.register(:binary, OID::Bytea, adapter: :ovirt_legacy_postgresql)
840
- ActiveRecord::Type.register(:cidr, OID::Cidr, adapter: :ovirt_legacy_postgresql)
841
- ActiveRecord::Type.register(:datetime, OID::DateTime, adapter: :ovirt_legacy_postgresql)
842
- ActiveRecord::Type.register(:decimal, OID::Decimal, adapter: :ovirt_legacy_postgresql)
843
- ActiveRecord::Type.register(:enum, OID::Enum, adapter: :ovirt_legacy_postgresql)
844
- ActiveRecord::Type.register(:hstore, OID::Hstore, adapter: :ovirt_legacy_postgresql)
845
- ActiveRecord::Type.register(:inet, OID::Inet, adapter: :ovirt_legacy_postgresql)
846
- ActiveRecord::Type.register(:json, OID::Json, adapter: :ovirt_legacy_postgresql)
847
- ActiveRecord::Type.register(:jsonb, OID::Jsonb, adapter: :ovirt_legacy_postgresql)
848
- ActiveRecord::Type.register(:money, OID::Money, adapter: :ovirt_legacy_postgresql)
849
- ActiveRecord::Type.register(:point, OID::Rails51Point, adapter: :ovirt_legacy_postgresql)
850
- ActiveRecord::Type.register(:legacy_point, OID::Point, adapter: :ovirt_legacy_postgresql)
851
- ActiveRecord::Type.register(:uuid, OID::Uuid, adapter: :ovirt_legacy_postgresql)
852
- ActiveRecord::Type.register(:vector, OID::Vector, adapter: :ovirt_legacy_postgresql)
853
- ActiveRecord::Type.register(:xml, OID::Xml, adapter: :ovirt_legacy_postgresql)
854
- end
855
- end
856
- end