activerecord-cockroachdb-adapter 0.3.0.beta1 → 6.0.0beta2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 48ca5207eae1d4bf1c26da960ad80e78f54099b1eb62466e89ff5186c9d41632
4
- data.tar.gz: d3d8a5c38a3adda552b283663d4aaf88e05587b3ac9e0da28418fa05d72cfcdd
3
+ metadata.gz: f2d3e8ca47a966406dd46a2516ee8e28d4634109974b012604efa5eeef41661c
4
+ data.tar.gz: 78f2720ce377db8b5d61c830c2f8819f4a928bceee3121668348293944b459ac
5
5
  SHA512:
6
- metadata.gz: 955dca7f67f68b93fd2ebd19cd6027de446598c823abbc108ce5630fbf6d566c14c09e283f37e3d04fb306ff72a68e55f6856daea91a61f9488636f9da957920
7
- data.tar.gz: 9cb056da8d37cd199427f2ae25d354bcb3fb606fde92e1c01fb2cef7eedd2122a9eccbf0b3693d7d0c7197f8ff8d7d4b1321d16d9212db4331673ddf5669b934
6
+ metadata.gz: 7928d308947df985eac1ecf4ac2338d8ed5e079c2436562a822b149699b0bc42e38411e90a73a5623c376613ca610060d68f9040d823b9fa65d2869baa98a6fd
7
+ data.tar.gz: 5421bb06b3185d331a0a43e700e2475f6ce28ee38d433ba28e3e82c4a0e361a94518dd46fd41b54d1a83f1a217d19e098eef0a251ad7e79803263c08bfd7e3dc
data/.gitignore CHANGED
@@ -7,3 +7,4 @@
7
7
  /pkg/
8
8
  /spec/reports/
9
9
  /tmp/
10
+ *.gem
@@ -46,19 +46,31 @@ Using [bundler](http://bundler.io/), install the dependancies of Rails.
46
46
  bundle install
47
47
  ```
48
48
 
49
- Then, to run the full ActiveRecord test suite with an active CockroachDB instance:
49
+ Then, to run the full test suite with an active CockroachDB instance:
50
50
 
51
51
  ```bash
52
52
  bundle exec rake test
53
53
  ```
54
54
 
55
- To run specific tests, set environemnt variable `TEST_FILES_AR`. For example, to run ActiveRecord tests `test/cases/associations_test.rb` and `test/cases/ar_schema_test.rb.rb`
55
+ To run specific ActiveRecord tests, set environemnt variable `TEST_FILES_AR`. For example, to run ActiveRecord tests `test/cases/associations_test.rb` and `test/cases/ar_schema_test.rb.rb`
56
56
 
57
57
  ```bash
58
58
  TEST_FILES_AR="test/cases/associations_test.rb,test/cases/ar_schema_test.rb" bundle exec rake test
59
59
  ```
60
60
 
61
- By default, tests will be run from the bundled version of Rails. To run against a local copy, set environemnt variable `RAILS_SOURCE`.
61
+ To run specific CockroachDB Adapter tests, set environemnt variable `TEST_FILES`. For example, to run CockroachDB Adpater tests `test/cases/adapter_test.rb` and `test/cases/associations/left_outer_join_association_test.rb`
62
+
63
+ ```bash
64
+ TEST_FILES="test/cases/adapter_test.rb,test/cases/associations/left_outer_join_association_test.rb" bundle exec rake test
65
+ ```
66
+
67
+ To run a specific test case, use minitest's `-n` option to run tests that match a given pattern. All minitest options are set via the `TESTOPTS` environemnt variable. For example, to run `test_indexes` from CockroachDB's `test/cases/adapter_test.rb` file
68
+
69
+ ```bash
70
+ TEST_FILES="test/cases/adapter_test.rb" TESTOPTS=`-n=/test_indexes/` bundle exec rake test
71
+ ```
72
+
73
+ By default, tests will be run from the bundled version of Rails. To run against a local copy, set environemnt variable `RAILS_SOURCE`. Running against a local copy of Rails can be helpful when try to debug issues.
62
74
 
63
75
  ```bash
64
76
  RAILS_SOURCE="path/to/local_copy" bundle exec rake test
data/README.md CHANGED
@@ -7,7 +7,7 @@ CockroachDB adapter for ActiveRecord 4 and 5. This is a lightweight extension of
7
7
  Add this line to your project's Gemfile:
8
8
 
9
9
  ```ruby
10
- gem 'activerecord-cockroachdb-adapter', '~> 0.2'
10
+ gem 'activerecord-cockroachdb-adapter', '~> 5.2.0'
11
11
  ```
12
12
 
13
13
  If you're using Rails 4.x, use the `0.1.x` versions of this gem.
@@ -4,7 +4,7 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
4
 
5
5
  Gem::Specification.new do |spec|
6
6
  spec.name = "activerecord-cockroachdb-adapter"
7
- spec.version = "0.3.0.beta1"
7
+ spec.version = "6.0.0beta2"
8
8
  spec.licenses = ["Apache-2.0"]
9
9
  spec.authors = ["Cockroach Labs"]
10
10
  spec.email = ["cockroach-db@googlegroups.com"]
@@ -13,7 +13,7 @@ Gem::Specification.new do |spec|
13
13
  spec.description = "Allows the use of CockroachDB as a backend for ActiveRecord and Rails apps."
14
14
  spec.homepage = "https://github.com/cockroachdb/activerecord-cockroachdb-adapter"
15
15
 
16
- spec.add_dependency "activerecord", "~> 5.2"
16
+ spec.add_dependency "activerecord", "~> 6.0.3"
17
17
  spec.add_dependency "pg", ">= 0.20"
18
18
 
19
19
  # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env bash
2
2
 
3
- set -euo pipefail
3
+ set -euox pipefail
4
4
 
5
5
  # Download CockroachDB
6
- VERSION=v20.1.0-rc.1
6
+ VERSION=v20.2.3
7
7
  wget -qO- https://binaries.cockroachdb.com/cockroach-$VERSION.linux-amd64.tgz | tar xvz
8
8
  readonly COCKROACH=./cockroach-$VERSION.linux-amd64/cockroach
9
9
 
@@ -21,7 +21,7 @@ run_cockroach() {
21
21
  cockroach quit --insecure || true
22
22
  rm -rf cockroach-data
23
23
  # Start CockroachDB.
24
- cockroach start --insecure --host=localhost --listening-url-file="$urlfile" >/dev/null 2>&1 &
24
+ cockroach start-single-node --max-sql-memory=25% --cache=25% --insecure --host=localhost --listening-url-file="$urlfile" >/dev/null 2>&1 &
25
25
  # Ensure CockroachDB is stopped on script exit.
26
26
  trap "echo 'Exit routine: Killing CockroachDB.' && kill -9 $! &> /dev/null" EXIT
27
27
  # Wait until CockroachDB has started.
@@ -33,6 +33,9 @@ run_cockroach() {
33
33
  done
34
34
  cockroach sql --insecure -e 'CREATE DATABASE activerecord_unittest;'
35
35
  cockroach sql --insecure -e 'CREATE DATABASE activerecord_unittest2;'
36
+ cockroach sql --insecure -e 'SET CLUSTER SETTING sql.stats.automatic_collection.enabled = false;'
37
+ cockroach sql --insecure -e 'SET CLUSTER SETTING sql.stats.histogram_collection.enabled = false;'
38
+ cockroach sql --insecure -e "SET CLUSTER SETTING jobs.retention_time = '180s';"
36
39
  }
37
40
 
38
41
  # Install ruby dependencies.
@@ -41,7 +44,7 @@ bundle install
41
44
 
42
45
  run_cockroach
43
46
 
44
- if ! (bundle exec rake test); then
47
+ if ! (RUBYOPT="-W0" TESTOPTS="-v" bundle exec rake test); then
45
48
  echo "Tests failed"
46
49
  HAS_FAILED=1
47
50
  else
data/docker.sh CHANGED
@@ -2,7 +2,7 @@
2
2
  #
3
3
  # This file is largely cargo-culted from cockroachdb/cockroach/build/builder.sh.
4
4
 
5
- set -euo pipefail
5
+ set -euox pipefail
6
6
 
7
7
  DOCKER_IMAGE_TAG=activerecord_test_container
8
8
 
@@ -15,6 +15,87 @@ module ActiveRecord
15
15
  serializable: "SERIALIZABLE"
16
16
  }
17
17
  end
18
+
19
+ # Overridden to avoid using transactions for schema creation.
20
+ def insert_fixtures_set(fixture_set, tables_to_delete = [])
21
+ fixture_inserts = build_fixture_statements(fixture_set)
22
+ table_deletes = tables_to_delete.map { |table| "DELETE FROM #{quote_table_name(table)}" }
23
+ statements = table_deletes + fixture_inserts
24
+
25
+ with_multi_statements do
26
+ disable_referential_integrity do
27
+ execute_batch(statements, "Fixtures Load")
28
+ end
29
+ end
30
+ end
31
+
32
+ private
33
+ def execute_batch(statements, name = nil)
34
+ statements.each do |statement|
35
+ execute(statement, name)
36
+ end
37
+ end
38
+
39
+ DEFAULT_INSERT_VALUE = Arel.sql("DEFAULT").freeze
40
+ private_constant :DEFAULT_INSERT_VALUE
41
+
42
+ def default_insert_value(column)
43
+ DEFAULT_INSERT_VALUE
44
+ end
45
+
46
+ def build_fixture_sql(fixtures, table_name)
47
+ columns = schema_cache.columns_hash(table_name)
48
+
49
+ values_list = fixtures.map do |fixture|
50
+ fixture = fixture.stringify_keys
51
+
52
+ unknown_columns = fixture.keys - columns.keys
53
+ if unknown_columns.any?
54
+ raise Fixture::FixtureError, %(table "#{table_name}" has no columns named #{unknown_columns.map(&:inspect).join(', ')}.)
55
+ end
56
+
57
+ columns.map do |name, column|
58
+ if fixture.key?(name)
59
+ type = lookup_cast_type_from_column(column)
60
+ with_yaml_fallback(type.serialize(fixture[name]))
61
+ else
62
+ default_insert_value(column)
63
+ end
64
+ end
65
+ end
66
+
67
+ table = Arel::Table.new(table_name)
68
+ manager = Arel::InsertManager.new
69
+ manager.into(table)
70
+
71
+ if values_list.size == 1
72
+ values = values_list.shift
73
+ new_values = []
74
+ columns.each_key.with_index { |column, i|
75
+ unless values[i].equal?(DEFAULT_INSERT_VALUE)
76
+ new_values << values[i]
77
+ manager.columns << table[column]
78
+ end
79
+ }
80
+ values_list << new_values
81
+ else
82
+ columns.each_key { |column| manager.columns << table[column] }
83
+ end
84
+
85
+ manager.values = manager.create_values_list(values_list)
86
+ manager.to_sql
87
+ end
88
+
89
+ def build_fixture_statements(fixture_set)
90
+ fixture_set.map do |table_name, fixtures|
91
+ next if fixtures.empty?
92
+ build_fixture_sql(fixtures, table_name)
93
+ end.compact
94
+ end
95
+
96
+ def with_multi_statements
97
+ yield
98
+ end
18
99
  end
19
100
  end
20
101
  end
@@ -38,6 +38,54 @@ module ActiveRecord
38
38
  def default_sequence_name(table_name, pk = "id")
39
39
  nil
40
40
  end
41
+
42
+ # CockroachDB will use INT8 if the SQL type is INTEGER, so we make it use
43
+ # INT4 explicitly when needed.
44
+ def type_to_sql(type, limit: nil, precision: nil, scale: nil, array: nil, **) # :nodoc:
45
+ sql = \
46
+ case type.to_s
47
+ when "integer"
48
+ case limit
49
+ when nil; "int"
50
+ when 1, 2; "int2"
51
+ when 3, 4; "int4"
52
+ when 5..8; "int8"
53
+ else super
54
+ end
55
+ else
56
+ super
57
+ end
58
+ # The call to super might have appeneded [] already.
59
+ if array && type != :primary_key && !sql.end_with?("[]")
60
+ sql = "#{sql}[]"
61
+ end
62
+ sql
63
+ end
64
+
65
+ # This overrides the method from PostegreSQL adapter
66
+ # Resets the sequence of a table's primary key to the maximum value.
67
+ def reset_pk_sequence!(table, pk = nil, sequence = nil)
68
+ unless pk && sequence
69
+ default_pk, default_sequence = pk_and_sequence_for(table)
70
+
71
+ pk ||= default_pk
72
+ sequence ||= default_sequence
73
+ end
74
+
75
+ if @logger && pk && !sequence
76
+ @logger.warn "#{table} has primary key #{pk} with no default sequence."
77
+ end
78
+
79
+ if pk && sequence
80
+ quoted_sequence = quote_table_name(sequence)
81
+ max_pk = query_value("SELECT MAX(#{quote_column_name pk}) FROM #{quote_table_name(table)}", "SCHEMA")
82
+ if max_pk.nil?
83
+ minvalue = query_value("SELECT seqmin FROM pg_sequence WHERE seqrelid = #{quote(quoted_sequence)}::regclass", "SCHEMA")
84
+ end
85
+
86
+ query_value("SELECT setval(#{quote(quoted_sequence)}, #{max_pk ? max_pk : minvalue}, #{max_pk ? true : false})", "SCHEMA")
87
+ end
88
+ end
41
89
  end
42
90
  end
43
91
  end
@@ -8,16 +8,22 @@ module ActiveRecord
8
8
  # transactions that fail due to serialization errors. Failed
9
9
  # transactions will be retried until they pass or the max retry limit is
10
10
  # exceeded.
11
- def within_new_transaction(options = {})
12
- attempts = options.fetch(:attempts, 0)
11
+ def within_new_transaction(isolation: nil, joinable: true, attempts: 0)
13
12
  super
14
- rescue ActiveRecord::SerializationFailure => error
13
+ rescue ActiveRecord::StatementInvalid => error
14
+ raise unless retryable? error
15
15
  raise if attempts >= @connection.max_transaction_retries
16
16
 
17
17
  attempts += 1
18
18
  sleep_seconds = (2 ** attempts + rand) / 10
19
19
  sleep(sleep_seconds)
20
- within_new_transaction(options.merge(attempts: attempts)) { yield }
20
+ within_new_transaction(isolation: isolation, joinable: joinable, attempts: attempts) { yield }
21
+ end
22
+
23
+ def retryable?(error)
24
+ return true if error.is_a? ActiveRecord::SerializationFailure
25
+ return retryable? error.cause if error.cause
26
+ false
21
27
  end
22
28
  end
23
29
  end
@@ -24,10 +24,14 @@ module ActiveRecord
24
24
  valid_conn_param_keys = PG::Connection.conndefaults_hash.keys + [:sslmode, :application_name]
25
25
  conn_params.slice!(*valid_conn_param_keys)
26
26
 
27
- # The postgres drivers don't allow the creation of an unconnected
28
- # PG::Connection object, so just pass a nil connection object for the
29
- # time being.
30
- ConnectionAdapters::CockroachDBAdapter.new(nil, logger, conn_params, config)
27
+ conn = PG.connect(conn_params)
28
+ ConnectionAdapters::CockroachDBAdapter.new(conn, logger, conn_params, config)
29
+ rescue ::PG::Error, ActiveRecord::ActiveRecordError => error
30
+ if error.message.include?("does not exist")
31
+ raise ActiveRecord::NoDatabaseError
32
+ else
33
+ raise
34
+ end
31
35
  end
32
36
  end
33
37
  end
@@ -56,6 +60,10 @@ module ActiveRecord
56
60
  100000
57
61
  end
58
62
 
63
+ def supports_bulk_alter?
64
+ false
65
+ end
66
+
59
67
  def supports_json?
60
68
  # FIXME(joey): Add a version check.
61
69
  true
@@ -69,18 +77,12 @@ module ActiveRecord
69
77
  false
70
78
  end
71
79
 
72
- def supports_ranges?
73
- # See cockroachdb/cockroach#17022
74
- false
75
- end
76
-
77
80
  def supports_materialized_views?
78
81
  false
79
82
  end
80
83
 
81
84
  def supports_partial_index?
82
- # See cockroachdb/cockroach#9683
83
- false
85
+ @crdb_version >= 202
84
86
  end
85
87
 
86
88
  def supports_expression_index?
@@ -103,8 +105,7 @@ module ActiveRecord
103
105
  end
104
106
 
105
107
  def supports_advisory_locks?
106
- # FIXME(joey): We may want to make this false.
107
- true
108
+ false
108
109
  end
109
110
 
110
111
  def supports_virtual_columns?
@@ -112,6 +113,10 @@ module ActiveRecord
112
113
  false
113
114
  end
114
115
 
116
+ def supports_string_to_array_coercion?
117
+ @crdb_version >= 202
118
+ end
119
+
115
120
  # This is hardcoded to 63 (as previously was in ActiveRecord 5.0) to aid in
116
121
  # migration from PostgreSQL to CockroachDB. In practice, this limitation
117
122
  # is arbitrary since CockroachDB supports index name lengths and table alias
@@ -127,6 +132,31 @@ module ActiveRecord
127
132
  alias index_name_length max_identifier_length
128
133
  alias table_alias_length max_identifier_length
129
134
 
135
+ def initialize(connection, logger, conn_params, config)
136
+ super(connection, logger, conn_params, config)
137
+ crdb_version_string = query_value("SHOW crdb_version")
138
+ if crdb_version_string.include? "v1."
139
+ version_num = 1
140
+ elsif crdb_version_string.include? "v2."
141
+ version_num 2
142
+ elsif crdb_version_string.include? "v19.1."
143
+ version_num = 191
144
+ elsif crdb_version_string.include? "v19.2."
145
+ version_num = 192
146
+ elsif crdb_version_string.include? "v20.1."
147
+ version_num = 201
148
+ else
149
+ version_num = 202
150
+ end
151
+ @crdb_version = version_num
152
+ end
153
+
154
+ def self.database_exists?(config)
155
+ !!ActiveRecord::Base.cockroachdb_connection(config)
156
+ rescue ActiveRecord::NoDatabaseError
157
+ false
158
+ end
159
+
130
160
  private
131
161
 
132
162
  def initialize_type_map(m = type_map)
@@ -199,7 +229,8 @@ module ActiveRecord
199
229
  def extract_value_from_default(default)
200
230
  super ||
201
231
  extract_escaped_string_from_default(default) ||
202
- extract_time_from_default(default)
232
+ extract_time_from_default(default) ||
233
+ extract_empty_array_from_default(default)
203
234
  end
204
235
 
205
236
  # Both PostgreSQL and CockroachDB use C-style string escapes under the
@@ -241,6 +272,15 @@ module ActiveRecord
241
272
  nil
242
273
  end
243
274
 
275
+ # CockroachDB stores default values for arrays in the `ARRAY[...]` format.
276
+ # In general, it is hard to parse that, but it is easy to handle the common
277
+ # case of an empty array.
278
+ def extract_empty_array_from_default(default)
279
+ return unless supports_string_to_array_coercion?
280
+ return unless default =~ /\AARRAY\[\]\z/
281
+ return "{}"
282
+ end
283
+
244
284
  # end private
245
285
  end
246
286
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activerecord-cockroachdb-adapter
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0.beta1
4
+ version: 6.0.0beta2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Cockroach Labs
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-04-17 00:00:00.000000000 Z
11
+ date: 2020-12-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '5.2'
19
+ version: 6.0.3
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '5.2'
26
+ version: 6.0.3
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: pg
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -77,7 +77,7 @@ licenses:
77
77
  - Apache-2.0
78
78
  metadata:
79
79
  allowed_push_host: https://rubygems.org
80
- post_install_message:
80
+ post_install_message:
81
81
  rdoc_options: []
82
82
  require_paths:
83
83
  - lib
@@ -92,8 +92,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
92
92
  - !ruby/object:Gem::Version
93
93
  version: 1.3.1
94
94
  requirements: []
95
- rubygems_version: 3.1.2
96
- signing_key:
95
+ rubygems_version: 3.1.4
96
+ signing_key:
97
97
  specification_version: 4
98
98
  summary: CockroachDB adapter for ActiveRecord.
99
99
  test_files: []