activerecord-cockroachdb-adapter 7.0.2 → 7.0.3

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: a24349ecd71877aabdf47013dd73406e4bb16c0495fee7295e11656a6eea20b4
4
- data.tar.gz: 1bf5fe5b0d56f12249090edfd84bd56a81ac18f05670259a7473d780648045e5
3
+ metadata.gz: 594ac59a7a678d5619ea771e3130a481481560adcc88032d7c17d61b6dabc279
4
+ data.tar.gz: 0cd6ea6bd6a1fe808142a91e35a24d4e15a59b48f56e4dba37d8a25584dd7a8a
5
5
  SHA512:
6
- metadata.gz: 24bd03ffa04bdd43827923fbe411b8d05e74fad8f9bedd54511de71593d06a2b40e5289a15bc84d09ec9002b949a5b0e91a168cba3b451d003e54f36ecfa758a
7
- data.tar.gz: 90dd57aee1a18163fc18b9027f45dbed3045183de4bc2d098cc895e90d3723c4512f00dce1255263dd08b11b449adc5f84f6de4da4a07ac9802e82ed22d571b0
6
+ metadata.gz: 56205a247976012b6a25691045a773ca9fefecc1b98bb47ee83fb85dffede72a8aed5bc605a332d00b31fb57f67ee1018b850d14638581454980a6b96d2e8865
7
+ data.tar.gz: 92515e498cc8cedfe284cfe4cf9b3cb87eae72630aa5828e5983dfabe488ad214386561a0fb8072963b0796559209ee033cb010495d263d44eb1abf048afaa37
@@ -0,0 +1,86 @@
1
+ # Inspired from:
2
+ # - https://github.com/cockroachdb/sqlalchemy-cockroachdb/blob/master/.github/workflows/ci.yml
3
+ # - https://github.com/rgeo/activerecord-postgis-adapter/blob/master/.github/workflows/tests.yml
4
+ name: Test
5
+
6
+ on:
7
+ # Triggers the workflow on push or pull request events.
8
+ push:
9
+ # This should disable running the workflow on tags, according to the
10
+ # on.<push|pull_request>.<branches|tags> GitHub Actions docs.
11
+ branches:
12
+ - "*"
13
+ pull_request:
14
+ types: [opened, reopened, synchronize]
15
+
16
+ # Allows you to run this workflow manually from the Actions tab
17
+ workflow_dispatch:
18
+
19
+ # This allows a subsequently queued workflow run to interrupt previous runs.
20
+ concurrency:
21
+ group: '${{ github.workflow }} @ ${{ github.event.pull_request.head.label || github.head_ref || github.ref }}'
22
+ cancel-in-progress: true
23
+
24
+ jobs:
25
+ test:
26
+ runs-on: ubuntu-latest
27
+ strategy:
28
+ matrix:
29
+ crdb: [v23.1.5]
30
+ ruby: [ruby-head]
31
+ name: Test (crdb=${{ matrix.crdb }} ruby=${{ matrix.ruby }})
32
+ steps:
33
+ - name: Set Up Actions
34
+ uses: actions/checkout@v3
35
+ - name: Install GEOS
36
+ run: sudo apt-get install libgeos-dev
37
+ - name: Set Up Ruby
38
+ uses: ruby/setup-ruby@v1
39
+ with:
40
+ ruby-version: ${{ matrix.ruby }}
41
+ bundler-cache: true
42
+ - name: Install and Start Cockroachdb
43
+ run: |
44
+ # Download CockroachDB
45
+ wget -qO- https://binaries.cockroachdb.com/cockroach-${{ matrix.crdb }}.linux-amd64.tgz | tar xvz
46
+
47
+ export PATH=./cockroach-${{ matrix.crdb }}.linux-amd64/:$PATH
48
+ readonly urlfile=cockroach-url
49
+
50
+ # Start a CockroachDB server and wait for it to become ready.
51
+ rm -f "$urlfile"
52
+ rm -rf cockroach-data
53
+ # Start CockroachDB.
54
+ cockroach start-single-node --max-sql-memory=25% --cache=25% --insecure --host=localhost --spatial-libs=./cockroach-${{ matrix.crdb }}.linux-amd64/lib --listening-url-file="$urlfile" >/dev/null 2>&1 &
55
+ # Ensure CockroachDB is stopped on script exit.
56
+ # Wait until CockroachDB has started.
57
+ for i in {0..3}; do
58
+ [[ -f "$urlfile" ]] && break
59
+ backoff=$((2 ** i))
60
+ echo "server not yet available; sleeping for $backoff seconds"
61
+ sleep $backoff
62
+ done
63
+ cockroach sql --insecure -e "
64
+ CREATE DATABASE activerecord_unittest;
65
+ CREATE DATABASE activerecord_unittest2;
66
+ SET CLUSTER SETTING sql.stats.automatic_collection.enabled = false;
67
+ SET CLUSTER SETTING sql.stats.histogram_collection.enabled = false;
68
+ SET CLUSTER SETTING jobs.retention_time = '180s';
69
+ SET CLUSTER SETTING sql.defaults.experimental_alter_column_type.enabled = 'true';
70
+
71
+ ALTER RANGE default CONFIGURE ZONE USING num_replicas = 1, gc.ttlseconds = 30;
72
+ ALTER TABLE system.public.jobs CONFIGURE ZONE USING num_replicas = 1, gc.ttlseconds = 30;
73
+ ALTER RANGE meta CONFIGURE ZONE USING num_replicas = 1, gc.ttlseconds = 30;
74
+ ALTER RANGE system CONFIGURE ZONE USING num_replicas = 1, gc.ttlseconds = 30;
75
+ ALTER RANGE liveness CONFIGURE ZONE USING num_replicas = 1, gc.ttlseconds = 30;
76
+
77
+ SET CLUSTER SETTING kv.range_merge.queue_interval = '50ms';
78
+ SET CLUSTER SETTING kv.raft_log.disable_synchronization_unsafe = 'true';
79
+ SET CLUSTER SETTING jobs.registry.interval.cancel = '180s';
80
+ SET CLUSTER SETTING jobs.registry.interval.gc = '30s';
81
+ SET CLUSTER SETTING kv.range_split.by_load_merge_delay = '5s';
82
+
83
+ SET CLUSTER SETTING sql.defaults.experimental_temporary_tables.enabled = 'true';
84
+ "
85
+ - name: Test
86
+ run: bundle exec rake test
data/CHANGELOG.md CHANGED
@@ -1,5 +1,14 @@
1
1
  # Changelog
2
2
 
3
+ ## Ongoing
4
+
5
+ ## 7.0.3 - 2023-08-23
6
+
7
+ - Fix Multiple Database connections ([#283](https://github.com/cockroachdb/activerecord-cockroachdb-adapter/pull/)).
8
+ - Add support for sql load in rake tasks ([#275](https://github.com/cockroachdb/activerecord-cockroachdb-adapter/pull/)).
9
+ - Add support for sql dump in rake tasks ([#273](https://github.com/cockroachdb/activerecord-cockroachdb-adapter/pull/)).
10
+ - Add support for table optimize hints ([#266](https://github.com/cockroachdb/activerecord-cockroachdb-adapter/pull/)).
11
+
3
12
  ## 7.0.2 - 2023-05-23
4
13
 
5
14
  - Fix default numbers test to expect the correct result after
data/CONTRIBUTING.md CHANGED
@@ -15,12 +15,15 @@ override and monkey-patch functionality.
15
15
 
16
16
  ## Setup and running tests
17
17
 
18
- In CockroachDB, create two databases to be used by the ActiveRecord test suite:
18
+ ### CockroachDB
19
+
20
+ First, You should setup a cockroachdb local instance. You can use the
21
+ `bin/start-cockroachdb` to help you with that task. Otherwise, once setup,
22
+ create two databases to be used by the ActiveRecord test suite:
19
23
  activerecord_unittest and activerecord_unittest2.
20
24
 
21
25
  ```sql
22
26
  CREATE DATABASE activerecord_unittest;
23
-
24
27
  CREATE DATABASE activerecord_unittest2;
25
28
  ```
26
29
 
@@ -87,12 +90,6 @@ To run a specific test case, use minitest's `-n` option to run tests that match
87
90
  TEST_FILES="test/cases/adapter_test.rb" TESTOPTS=`-n=/test_indexes/` bundle exec rake test
88
91
  ```
89
92
 
90
- 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.
91
-
92
- ```bash
93
- RAILS_SOURCE="path/to/local_copy" bundle exec rake test
94
- ```
95
-
96
93
  `test/config.yml` assumes CockroachDB will be running at localhost:26257 with a root user. Make changes to `test/config.yml` as needed.
97
94
 
98
95
  ### Run Tests from a Backup
@@ -117,23 +114,6 @@ And the `activerecord_unittest` database will use the `RESTORE` command to load
117
114
 
118
115
  # Improvements
119
116
 
120
-
121
- ## Support past Rails versions
122
-
123
- Currently, only a beta version of Rails is tested. This means that the
124
- adapter has been modified in to accommodate unreleased changes. In order
125
- to run the tests for Rails 5.1 or 4.2, the test changes will need to be
126
- cherry-picked back. Conflicts are mostly only expected for tests that
127
- have not yet been added.
128
-
129
- Sadly, this does mean that we will have to have multiple versions of the
130
- driver for the multiple versions of Rails.
131
-
132
- A proposal for the CockroachDB adapter versioning would be to follow
133
- ActiveRecord minor versions. For example, if you use Rails 4.2.5, you
134
- would specify the CockroachDB version `~> 4.2.0`.
135
-
136
-
137
117
  ## Running CI automatically
138
118
 
139
119
  Currently the fork is set up to run using TeamCity only on the current
@@ -247,7 +227,7 @@ need to be cleaned up.
247
227
 
248
228
  # Notes for the non-Rubyer
249
229
 
250
- rvm is an environment manager that lets you manage and swap between
230
+ rbenv is an environment manager that lets you manage and swap between
251
231
  multiple versions of Ruby and their dependencies.
252
232
 
253
233
  bundle is dependency manager that uses a projects `Gemfile` (and often
data/Gemfile CHANGED
@@ -1,58 +1,49 @@
1
- require 'openssl'
2
- source 'https://rubygems.org'
3
- gemspec
1
+ # frozen_string_literal: true
4
2
 
5
- if ENV['RAILS_SOURCE']
6
- gemspec path: ENV['RAILS_SOURCE']
7
- else
8
- def get_version_from_gemspec
9
- gemspec = eval(File.read('activerecord-cockroachdb-adapter.gemspec'))
3
+ source "https://rubygems.org"
10
4
 
11
- gem_version = gemspec.dependencies.
12
- find { |dep| dep.name == 'activerecord' }.
13
- requirement.
14
- requirements.
15
- first.
16
- last
5
+ gemspec
17
6
 
18
- major, minor, tiny, pre = gem_version.segments
19
7
 
20
- if pre
21
- gem_version.to_s
22
- else
23
- find_latest_matching_version(major, minor)
8
+ module RailsTag
9
+ class << self
10
+ def call
11
+ req = gemspec_requirement
12
+ "v" + all_activerecord_versions.find { req.satisfied_by?(_1) }.version
24
13
  end
25
- end
26
14
 
27
- def find_latest_matching_version(gemspec_major, gemspec_minor)
28
- all_activerecord_versions.
29
- reject { |version| version["prerelease"] }.
30
- map { |version| version["number"].split(".").map(&:to_i) }.
31
- find { |major, minor|
32
- major == gemspec_major && (minor == gemspec_minor || gemspec_minor.nil?)
33
- }.join(".")
34
- end
15
+ def gemspec_requirement
16
+ File
17
+ .foreach(File.expand_path("activerecord-cockroachdb-adapter.gemspec", __dir__), chomp: true)
18
+ .find { _1[/add_dependency\s.activerecord.,\s.(.*)./] }
19
+
20
+ Gem::Requirement.new(Regexp.last_match(1))
21
+ end
35
22
 
36
- def all_activerecord_versions
37
- require 'net/http'
38
- require 'yaml'
23
+ def all_activerecord_versions
24
+ require 'net/http'
25
+ require 'yaml'
39
26
 
40
- uri = URI.parse "https://rubygems.org/api/v1/versions/activerecord.yaml"
41
- http = Net::HTTP.new(uri.host, uri.port)
42
- http.use_ssl = true
43
- http.verify_mode = OpenSSL::SSL::VERIFY_NONE
27
+ uri = URI.parse "https://rubygems.org/api/v1/versions/activerecord.yaml"
28
+ http = Net::HTTP.new(uri.host, uri.port)
29
+ http.use_ssl = true
30
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
44
31
 
45
- YAML.load(
46
- http.request(Net::HTTP::Get.new(uri.request_uri)).body
47
- )
32
+ YAML.load(
33
+ http.request(Net::HTTP::Get.new(uri.request_uri)).body
34
+ ).map { Gem::Version.new(_1["number"]) }
35
+ end
48
36
  end
49
-
50
- # Get Rails from source because the gem doesn't include tests
51
- version = ENV['RAILS_VERSION'] || get_version_from_gemspec
52
- gem 'rails', git: "https://github.com/rails/rails.git", tag: "v#{version}"
53
37
  end
54
38
 
55
- group :development do
39
+
40
+ group :development, :test do
41
+ # We need to load the gem from git to have access to activerecord's test files.
42
+ # You can use `path: "some/local/rails"` if you want to test the gem against
43
+ # a specific rails codebase.
44
+ gem "rails", github: "rails/rails", tag: RailsTag.call
45
+
46
+ gem "rake"
56
47
  gem "byebug"
57
48
  gem "minitest-excludes", "~> 2.0.1"
58
49
 
data/README.md CHANGED
@@ -321,7 +321,7 @@ p modified_fac.parse_wkt(wkt)
321
321
  #=> #<RGeo::Geographic::SphericalPolygonImpl>
322
322
  ```
323
323
 
324
- Be careful when performing calculations on potentially invalid geometries, as the results might be nonsensical. For example, the area returned of an hourglass made of 2 equivalent triangles with a self-intersection in the middle is 0.
324
+ Be careful when performing calculations on potentially invalid geometries, as the results might be nonsensical. For example, the area returned of an hourglass made of 2 equivalent triangles with a self-intersection in the middle is 0.
325
325
 
326
326
  Note that when using the `spherical_factory`, there is a chance that valid geometries will be interpreted as invalid due to floating point issues with small geometries.
327
327
 
data/Rakefile CHANGED
@@ -4,14 +4,12 @@ require_relative 'test/support/paths_cockroachdb'
4
4
  require_relative 'test/support/rake_helpers'
5
5
  require_relative 'test/support/template_creator'
6
6
 
7
- task test: ["test:cockroachdb"]
8
7
  task default: [:test]
9
8
 
10
9
  namespace :db do
11
10
  task "create_test_template" do
12
11
  ENV['DEBUG_COCKROACHDB_ADAPTER'] = "1"
13
12
  ENV['COCKROACH_SKIP_LOAD_SCHEMA'] = "1"
14
- ENV["ARCONN"] = "cockroachdb"
15
13
 
16
14
  TemplateCreator.connect
17
15
  require_relative 'test/cases/helper'
@@ -26,17 +24,9 @@ namespace :db do
26
24
  end
27
25
  end
28
26
 
29
- namespace :test do
30
- Rake::TestTask.new("cockroachdb") do |t|
31
- t.libs = ARTest::CockroachDB.test_load_paths
32
- t.test_files = test_files
33
- t.warning = !!ENV["WARNING"]
34
- t.verbose = false
35
- end
36
-
37
- task "cockroachdb:env" do
38
- ENV["ARCONN"] = "cockroachdb"
39
- end
27
+ Rake::TestTask.new do |t|
28
+ t.libs = ARTest::CockroachDB.test_load_paths
29
+ t.test_files = RakeHelpers.test_files
30
+ t.warning = !!ENV["WARNING"]
31
+ t.verbose = false
40
32
  end
41
-
42
- task 'test:cockroachdb' => 'test:cockroachdb:env'
@@ -1,9 +1,6 @@
1
- # coding: utf-8
1
+ # frozen_string_literal: true
2
2
 
3
- lib = File.expand_path('lib', __dir__)
4
- $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
-
6
- require './lib/version.rb'
3
+ require_relative 'lib/version'
7
4
  version = ActiveRecord::COCKROACH_DB_ADAPTER_VERSION
8
5
 
9
6
  Gem::Specification.new do |spec|
data/bin/console CHANGED
@@ -1,14 +1,50 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- require "bundler/setup"
4
- require "activerecord/cockroachdb"
3
+ $:.unshift(File.expand_path("../lib", __dir__))
5
4
 
6
- # You can add fixtures and/or initialization code here to make experimenting
7
- # with your gem easier. You can also use a different console, if you like.
5
+ # require "bundler/setup"
6
+ # Bundler.require :development
8
7
 
9
- # (If you use this, don't forget to add pry to your Gemfile!)
10
- # require "pry"
11
- # Pry.start
8
+ require "active_record"
9
+ # This allows playing with the rake task as well. Ex:
10
+ #
11
+ # ActiveRecord::Tasks::DatabaseTasks.
12
+ # structure_load(Post.connection_db_config, "awesome-file.sql")
13
+ require "active_record/connection_adapters/cockroachdb/database_tasks"
14
+
15
+ begin
16
+ retried = false
17
+ ActiveRecord::Base.establish_connection(
18
+ #Alternative version: "cockroachdb://root@localhost:26257/ar_crdb_console"
19
+ adapter: "cockroachdb",
20
+ host: "localhost",
21
+ port: 26257,
22
+ user: "root",
23
+ database: "ar_crdb_console"
24
+ )
25
+ ActiveRecord::Base.connection
26
+ rescue ActiveRecord::NoDatabaseError
27
+ raise if retried
28
+ system("cockroach sql --insecure --host=localhost:26257 --execute='create database ar_crdb_console'",
29
+ exception: true)
30
+ retried = true
31
+ retry
32
+ end
33
+
34
+ class Post < ActiveRecord::Base
35
+ end
36
+
37
+ unless Post.table_exists?
38
+ migration = Class.new(ActiveRecord::Migration::Current) do
39
+ def up
40
+ create_table("posts") do |t|
41
+ t.string :title
42
+ t.text :body
43
+ end
44
+ end
45
+ end
46
+ migration.migrate(:up)
47
+ end
12
48
 
13
49
  require "irb"
14
50
  IRB.start(__FILE__)
@@ -0,0 +1,48 @@
1
+ #!/usr/bin/env zsh
2
+
3
+ set -eu
4
+
5
+ die() { echo "$0: $*" 1>&2 ; false; }
6
+
7
+ root_dir="$(dirname $(dirname "$0:A"))"
8
+ pid_file="$root_dir/tmp/cockroach.pid"
9
+ log_file="$root_dir/tmp/cockroachdb.log"
10
+
11
+ mkdir -p "$root_dir/tmp"
12
+ rm -f "$pid_file"
13
+
14
+ if ! (( ${+commands[cockroach]} )); then
15
+ die 'the `cockroach` toolchain is not installed.
16
+ See https://www.cockroachlabs.com/docs/stable/install-cockroachdb.html'
17
+ fi
18
+
19
+ cockroach start-single-node \
20
+ --insecure --store=type=mem,size=0.25 --advertise-addr=localhost --pid-file "$pid_file" \
21
+ &> "$log_file" &
22
+
23
+ cockroach_pid=$!
24
+
25
+ until [[ -f "$pid_file" ]]; do
26
+ sleep 1
27
+ done
28
+
29
+
30
+ cat <<-SQL | cockroach sql --insecure --host=localhost:26257 > /dev/null
31
+ -- https://www.cockroachlabs.com/docs/stable/local-testing.html
32
+ SET CLUSTER SETTING kv.raft_log.disable_synchronization_unsafe = true;
33
+ SET CLUSTER SETTING kv.range_merge.queue_interval = '50ms';
34
+ SET CLUSTER SETTING jobs.registry.interval.gc = '30s';
35
+ SET CLUSTER SETTING jobs.registry.interval.cancel = '180s';
36
+ SET CLUSTER SETTING jobs.retention_time = '15s';
37
+ SET CLUSTER SETTING sql.stats.automatic_collection.enabled = false;
38
+ SET CLUSTER SETTING kv.range_split.by_load_merge_delay = '5s';
39
+ ALTER RANGE default CONFIGURE ZONE USING "gc.ttlseconds" = 600;
40
+ ALTER DATABASE system CONFIGURE ZONE USING "gc.ttlseconds" = 600;
41
+
42
+ CREATE DATABASE activerecord_unittest;
43
+ CREATE DATABASE activerecord_unittest2;
44
+ SQL
45
+
46
+ tail -f "$log_file"
47
+
48
+ trap "kill $cockroach_pid" EXIT
@@ -70,12 +70,7 @@ run_cockroach
70
70
 
71
71
  if ! (RUBYOPT="-W0" TESTOPTS="-v" bundle exec rake test); then
72
72
  echo "Tests failed"
73
- HAS_FAILED=1
74
- else
75
- echo "Tests passed"
76
- HAS_FAILED=0
73
+ exit 1
77
74
  fi
78
75
 
79
- if [ $HAS_FAILED -eq 1 ]; then
80
- exit 1
81
- fi
76
+ echo "Tests passed"
@@ -1,7 +1,7 @@
1
1
  module ActiveRecord
2
2
  module ConnectionAdapters
3
3
  module CockroachDB
4
- module PostgreSQLColumnMonkeyPatch
4
+ class Column < PostgreSQLColumn
5
5
  # most functions taken from activerecord-postgis-adapter spatial_column
6
6
  # https://github.com/rgeo/activerecord-postgis-adapter/blob/master/lib/active_record/connection_adapters/postgis/spatial_column.rb
7
7
  def initialize(name, default, sql_type_metadata = nil, null = true,
@@ -93,9 +93,5 @@ module ActiveRecord
93
93
  end
94
94
  end
95
95
  end
96
-
97
- class PostgreSQLColumn
98
- prepend CockroachDB::PostgreSQLColumnMonkeyPatch
99
- end
100
96
  end
101
97
  end
@@ -5,12 +5,89 @@ module ActiveRecord
5
5
  module CockroachDB
6
6
  class DatabaseTasks < ActiveRecord::Tasks::PostgreSQLDatabaseTasks
7
7
  def structure_dump(filename, extra_flags=nil)
8
- raise "db:structure:dump is unimplemented. See https://github.com/cockroachdb/activerecord-cockroachdb-adapter/issues/2"
8
+ if extra_flags
9
+ raise "No flag supported yet, please raise an issue if needed. " \
10
+ "https://github.com/cockroachdb/activerecord-cockroachdb-adapter/issues/new"
11
+ end
12
+
13
+ case ActiveRecord.dump_schemas
14
+ when :all, String
15
+ raise "Custom schemas are not supported in CockroachDB. " \
16
+ "See https://github.com/cockroachdb/cockroach/issues/26443."
17
+ when :schema_search_path
18
+ if configuration_hash[:schema_search_path]
19
+ raise "Custom schemas are not supported in CockroachDB. " \
20
+ "See https://github.com/cockroachdb/cockroach/issues/26443."
21
+ end
22
+ end
23
+
24
+ conn = ActiveRecord::Base.connection
25
+ File.open(filename, "w") do |file|
26
+ %w(SCHEMAS TYPES).each do |object_kind|
27
+ ActiveRecord::Base.connection.execute("SHOW CREATE ALL #{object_kind}").each_row { file.puts _1 }
28
+ end
29
+
30
+ ignore_tables = ActiveRecord::SchemaDumper.ignore_tables.to_set
31
+
32
+ conn.execute("SHOW CREATE ALL TABLES").each_row do |(sql)|
33
+ if sql.start_with?("CREATE")
34
+ table_name = sql[/CREATE TABLE (?:.*?\.)?\"?(.*?)[\" ]/, 1]
35
+ next if ignore_tables.member?(table_name)
36
+ elsif sql.start_with?("ALTER")
37
+ table_name = sql[/ALTER TABLE (?:.*?\.)?\"?(.*?)[\" ]/, 1]
38
+ ref_table_name = sql[/REFERENCES (?:.*?\.)?\"?(.*?)[\" ]/, 1]
39
+ next if ignore_tables.member?(table_name) || ignore_tables.member?(ref_table_name)
40
+ end
41
+
42
+ file.puts sql
43
+ end
44
+ end
9
45
  end
10
46
 
11
47
  def structure_load(filename, extra_flags=nil)
12
- raise "db:structure:load is unimplemented. See https://github.com/cockroachdb/activerecord-cockroachdb-adapter/issues/2"
48
+ if extra_flags
49
+ raise "No flag supported yet, please raise an issue if needed. " \
50
+ "https://github.com/cockroachdb/activerecord-cockroachdb-adapter/issues/new"
51
+ end
52
+
53
+ run_cmd("cockroach", ["sql", "--set", "errexit=false", "--file", filename], "loading")
54
+ end
55
+
56
+ private
57
+
58
+ # Adapted from https://github.com/rails/rails/blob/a5fc471b3/activerecord/lib/active_record/tasks/postgresql_database_tasks.rb#L106.
59
+ # Using https://www.cockroachlabs.com/docs/stable/connection-parameters.html#additional-connection-parameters.
60
+ def cockroach_env
61
+ usr_pwd = ""
62
+ if configuration_hash[:username]
63
+ usr_pwd += configuration_hash[:username].to_s
64
+ if configuration_hash[:password]
65
+ usr_pwd += ":"
66
+ usr_pwd += configuration_hash[:password].to_s
67
+ end
68
+ usr_pwd += "@"
69
+ end
70
+
71
+ port = ""
72
+ port = ":#{configuration_hash[:port]}" if configuration_hash[:port]
73
+
74
+ params = %i(sslmode sslrootcert sslcert sslkey).filter_map do |key|
75
+ "#{key}=#{configuration_hash[key]}" if configuration_hash[key]
76
+ end.join("&")
77
+ params = "?#{params}" unless params.empty?
78
+
79
+ url = "postgres://#{usr_pwd}#{db_config.host}#{port}/#{db_config.database}#{params}"
80
+
81
+ {
82
+ # NOTE: sslmode in the url will take precedence over this setting, hence
83
+ # we don't need to conditionally set it.
84
+ "COCKROACH_INSECURE" => "true",
85
+ "COCKROACH_URL" => url
86
+ }
13
87
  end
88
+ # The `#run_cmd` method use `psql_env` to set environments variables.
89
+ # We override it with cockroach env variables.
90
+ alias_method :psql_env, :cockroach_env
14
91
  end
15
92
  end
16
93
  end
@@ -19,6 +19,12 @@ module ActiveRecord
19
19
  # converting to WKB, so this does it automatically.
20
20
  def quote(value)
21
21
  if value.is_a?(Numeric)
22
+ # NOTE: The fact that integers are quoted is important and helps
23
+ # mitigate a potential vulnerability.
24
+ #
25
+ # See
26
+ # - https://nvd.nist.gov/vuln/detail/CVE-2022-44566
27
+ # - https://github.com/cockroachdb/activerecord-cockroachdb-adapter/pull/280#discussion_r1288692977
22
28
  "'#{quote_string(value.to_s)}'"
23
29
  elsif RGeo::Feature::Geometry.check_type(value)
24
30
  "'#{RGeo::WKRep::WKBGenerator.new(hex_format: true, type_format: :ewkb, emit_ewkb_srid: true).generate(value)}'"
@@ -90,7 +90,7 @@ module ActiveRecord
90
90
  # {:dimension=>2, :has_m=>false, :has_z=>false, :name=>"latlon", :srid=>0, :type=>"GEOMETRY"}
91
91
  spatial = spatial_column_info(table_name).get(column_name, type_metadata.sql_type)
92
92
 
93
- PostgreSQL::Column.new(
93
+ CockroachDB::Column.new(
94
94
  column_name,
95
95
  default_value,
96
96
  type_metadata,
@@ -112,7 +112,7 @@ module ActiveRecord
112
112
  # since type alone is not enough to format the column.
113
113
  # Ex. type_to_sql(:geography, limit: "Point,4326")
114
114
  # => "geography(Point,4326)"
115
- #
115
+ #
116
116
  def type_to_sql(type, limit: nil, precision: nil, scale: nil, array: nil, **) # :nodoc:
117
117
  sql = \
118
118
  case type.to_s
@@ -4,8 +4,11 @@ module ActiveRecord
4
4
  # Return :postgresql instead of :cockroachdb for current_adapter_name so
5
5
  # we can continue using the ActiveRecord::Types defined in
6
6
  # PostgreSQLAdapter.
7
- def adapter_name_from(_model)
8
- :postgresql
7
+ def adapter_name_from(model)
8
+ name = model.connection_db_config.adapter.to_sym
9
+ return :postgresql if name == :cockroachdb
10
+
11
+ name
9
12
  end
10
13
  end
11
14
  end
@@ -23,6 +23,8 @@ require "active_record/connection_adapters/cockroachdb/arel_tosql"
23
23
  require_relative "../migration/cockroachdb/compatibility"
24
24
  require_relative "../../version"
25
25
 
26
+ require_relative "../relation/query_methods_ext"
27
+
26
28
  # Run to ignore spatial tables that will break schemna dumper.
27
29
  # Defined in ./setup.rb
28
30
  ActiveRecord::ConnectionAdapters::CockroachDB.initial_setup
@@ -0,0 +1,98 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ class Relation
5
+ module QueryMethodsExt
6
+ def from!(...) # :nodoc:
7
+ @force_index = nil
8
+ @index_hint = nil
9
+ super
10
+ end
11
+
12
+ # Set table index hint for the query to the
13
+ # given `index_name`, and `direction` (either
14
+ # `ASC` or `DESC`).
15
+ #
16
+ # Any call to `ActiveRecord::QueryMethods#from`
17
+ # will reset the index hint. Index hints are
18
+ # not set if the `from` clause is not a table
19
+ # name.
20
+ #
21
+ # @see https://www.cockroachlabs.com/docs/v22.2/table-expressions#force-index-selection
22
+ def force_index(index_name, direction: nil)
23
+ spawn.force_index!(index_name, direction: direction)
24
+ end
25
+
26
+ def force_index!(index_name, direction: nil)
27
+ return self unless from_clause_is_a_table_name?
28
+
29
+ index_name = sanitize_sql(index_name.to_s)
30
+ direction = direction.to_s.upcase
31
+ direction = %w[ASC DESC].include?(direction) ? ",#{direction}" : ""
32
+
33
+ @force_index = "FORCE_INDEX=#{index_name}#{direction}"
34
+ self.from_clause = build_from_clause_with_hints
35
+ self
36
+ end
37
+
38
+ # Set table index hint for the query with the
39
+ # given `hint`. This allows more control over
40
+ # the hint than `ActiveRecord::Relation#force_index`.
41
+ # For instance, you could set it to `NO_FULL_SCAN`.
42
+ #
43
+ # Any call to `ActiveRecord::QueryMethods#from`
44
+ # will reset the index hint. Index hints are
45
+ # not set if the `from` clause is not a table
46
+ # name.
47
+ #
48
+ # @see https://www.cockroachlabs.com/docs/v22.2/table-expressions#force-index-selection
49
+ def index_hint(hint)
50
+ spawn.index_hint!(hint)
51
+ end
52
+
53
+ def index_hint!(hint)
54
+ return self unless from_clause_is_a_table_name?
55
+
56
+ hint = sanitize_sql(hint.to_s)
57
+ @index_hint = hint.to_s
58
+ self.from_clause = build_from_clause_with_hints
59
+ self
60
+ end
61
+
62
+ private
63
+
64
+ def from_clause_is_a_table_name?
65
+ # if empty, we are just dealing with the current table.
66
+ return true if from_clause.empty?
67
+ # `from_clause` can be a subquery.
68
+ return false unless from_clause.value.is_a?(String)
69
+ # `from_clause` can be a list of tables or a function.
70
+ # A simple way to check is to see if the string
71
+ # contains special characters. But we have to
72
+ # not check against an existing table hint.
73
+ return !from_clause.value.gsub(/\@{.*?\}/, "").match?(/[,\(]/)
74
+ end
75
+
76
+ def build_from_clause_with_hints
77
+ table_hints = [@index_hint, @force_index].compact.join(",")
78
+
79
+ table_name =
80
+ if from_clause.empty?
81
+ quoted_table_name
82
+ else
83
+ # Remove previous table hints if any. And spaces.
84
+ from_clause.value.partition("@").first.strip
85
+ end
86
+ Relation::FromClause.new("#{table_name}@{#{table_hints}}", nil)
87
+ end
88
+ end
89
+
90
+ QueryMethods.prepend(QueryMethodsExt)
91
+ end
92
+ # `ActiveRecord::Base` ancestors do not include `QueryMethods`.
93
+ # But the `#all` method returns a relation, which has `QueryMethods`
94
+ # as ancestor. That is how active_record is doing is as well.
95
+ #
96
+ # @see https://github.com/rails/rails/blob/914130a9f/activerecord/lib/active_record/querying.rb#L23
97
+ Querying.delegate(:force_index, :index_hint, to: :all)
98
+ end
@@ -1,4 +1,4 @@
1
- if defined?(Rails)
1
+ if defined?(Rails::Railtie)
2
2
  module ActiveRecord
3
3
  module ConnectionAdapters
4
4
  class CockroachDBRailtie < ::Rails::Railtie
data/lib/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActiveRecord
4
- COCKROACH_DB_ADAPTER_VERSION = "7.0.2"
4
+ COCKROACH_DB_ADAPTER_VERSION = "7.0.3"
5
5
  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: 7.0.2
4
+ version: 7.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Cockroach Labs
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-05-30 00:00:00.000000000 Z
11
+ date: 2023-08-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -74,6 +74,7 @@ executables: []
74
74
  extensions: []
75
75
  extra_rdoc_files: []
76
76
  files:
77
+ - ".github/workflows/ci.yml"
77
78
  - ".github/workflows/docker.yml"
78
79
  - ".gitignore"
79
80
  - ".gitmodules"
@@ -87,6 +88,7 @@ files:
87
88
  - activerecord-cockroachdb-adapter.gemspec
88
89
  - bin/console
89
90
  - bin/setup
91
+ - bin/start-cockroachdb
90
92
  - build/Dockerfile
91
93
  - build/config.teamcity.yml
92
94
  - build/local-test.sh
@@ -114,6 +116,7 @@ files:
114
116
  - lib/active_record/connection_adapters/cockroachdb/type.rb
115
117
  - lib/active_record/connection_adapters/cockroachdb_adapter.rb
116
118
  - lib/active_record/migration/cockroachdb/compatibility.rb
119
+ - lib/active_record/relation/query_methods_ext.rb
117
120
  - lib/activerecord-cockroachdb-adapter.rb
118
121
  - lib/version.rb
119
122
  homepage: https://github.com/cockroachdb/activerecord-cockroachdb-adapter