activerecord-cockroachdb-adapter 7.0.3 → 7.1.0

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: 594ac59a7a678d5619ea771e3130a481481560adcc88032d7c17d61b6dabc279
4
- data.tar.gz: 0cd6ea6bd6a1fe808142a91e35a24d4e15a59b48f56e4dba37d8a25584dd7a8a
3
+ metadata.gz: 6c3f85e82470685a2ece6851d557bcaf03f5a77ce309d76062c3960ca17d1606
4
+ data.tar.gz: b6ab7bfb9d29feebf217b70b11ccf2f73e6fb0853138cc27f4556f07b6c36dac
5
5
  SHA512:
6
- metadata.gz: 56205a247976012b6a25691045a773ca9fefecc1b98bb47ee83fb85dffede72a8aed5bc605a332d00b31fb57f67ee1018b850d14638581454980a6b96d2e8865
7
- data.tar.gz: 92515e498cc8cedfe284cfe4cf9b3cb87eae72630aa5828e5983dfabe488ad214386561a0fb8072963b0796559209ee033cb010495d263d44eb1abf048afaa37
6
+ metadata.gz: 96fbb4c5a42c1ab3f2fe590f3c74012b883c818608c5b9d0525f30739f1d4d8733c95cfffd31a8a05350d6210d995f84d915727a494264d1cb1c217d10d200b1
7
+ data.tar.gz: 12ae86ab8f26eb074eabb60cd3f831eed44729d98f70bdc88d1c0d9c3d5cff5680ae7cf0a9643756395e6cfc1257cb7fc62ee5abab4c37c6f0fe45817fc619a0
data/.editorconfig ADDED
@@ -0,0 +1,7 @@
1
+ root = true
2
+
3
+ [*.rb]
4
+ indent_style = space
5
+ indent_size = 2
6
+ trim_trailing_whitespace = true
7
+ insert_final_newline = true
@@ -4,12 +4,9 @@
4
4
  name: Test
5
5
 
6
6
  on:
7
- # Triggers the workflow on push or pull request events.
8
7
  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
- - "*"
8
+ branches: [master]
9
+ # Triggers the workflow on pull request events.
13
10
  pull_request:
14
11
  types: [opened, reopened, synchronize]
15
12
 
@@ -18,27 +15,42 @@ on:
18
15
 
19
16
  # This allows a subsequently queued workflow run to interrupt previous runs.
20
17
  concurrency:
21
- group: '${{ github.workflow }} @ ${{ github.event.pull_request.head.label || github.head_ref || github.ref }}'
18
+ group: "${{ github.workflow }} @ ${{ github.ref }}"
22
19
  cancel-in-progress: true
23
20
 
24
21
  jobs:
22
+ # Since the name of the matrix job depends on the version, we define another job with a more stable name.
23
+ test_results:
24
+ if: ${{ always() }}
25
+ runs-on: ubuntu-latest
26
+ name: Test Results
27
+ needs: [test]
28
+ steps:
29
+ - run: |
30
+ result="${{ needs.test.result }}"
31
+ if [[ $result == "success" || $result == "skipped" ]]; then
32
+ exit 0
33
+ else
34
+ exit 1
35
+ fi
36
+
25
37
  test:
26
38
  runs-on: ubuntu-latest
27
39
  strategy:
28
40
  matrix:
29
- crdb: [v23.1.5]
30
- ruby: [ruby-head]
41
+ crdb: [v23.1.11]
42
+ ruby: [head]
31
43
  name: Test (crdb=${{ matrix.crdb }} ruby=${{ matrix.ruby }})
32
44
  steps:
33
45
  - name: Set Up Actions
34
- uses: actions/checkout@v3
46
+ uses: actions/checkout@v4
35
47
  - name: Install GEOS
36
48
  run: sudo apt-get install libgeos-dev
37
49
  - name: Set Up Ruby
38
50
  uses: ruby/setup-ruby@v1
39
51
  with:
40
- ruby-version: ${{ matrix.ruby }}
41
- bundler-cache: true
52
+ ruby-version: ${{ matrix.ruby }}
53
+ bundler-cache: true
42
54
  - name: Install and Start Cockroachdb
43
55
  run: |
44
56
  # Download CockroachDB
@@ -83,4 +95,4 @@ jobs:
83
95
  SET CLUSTER SETTING sql.defaults.experimental_temporary_tables.enabled = 'true';
84
96
  "
85
97
  - name: Test
86
- run: bundle exec rake test
98
+ run: bundle exec rake test TESTOPTS='--profile=3'
data/CHANGELOG.md CHANGED
@@ -2,6 +2,13 @@
2
2
 
3
3
  ## Ongoing
4
4
 
5
+ - Add support for [AOST](cockroachlabs.com/docs/stable/as-of-system-time) queries ([#284](https://github.com/cockroachdb/activerecord-cockroachdb-adapter/pull/284))
6
+ - Dump schema name in foreign keys ([#289](https://github.com/cockroachdb/activerecord-cockroachdb-adapter/pull/289))
7
+
8
+ ## 7.1.0 - 2024-01-30
9
+
10
+ - Add support for Rails 7.1 ([#300](https://github.com/cockroachdb/activerecord-cockroachdb-adapter/pull/300)).
11
+
5
12
  ## 7.0.3 - 2023-08-23
6
13
 
7
14
  - Fix Multiple Database connections ([#283](https://github.com/cockroachdb/activerecord-cockroachdb-adapter/pull/)).
data/Gemfile CHANGED
@@ -10,6 +10,11 @@ module RailsTag
10
10
  def call
11
11
  req = gemspec_requirement
12
12
  "v" + all_activerecord_versions.find { req.satisfied_by?(_1) }.version
13
+ rescue => e
14
+ warn "Unable to determine Rails version. Using last used. Error: #{e.message}"
15
+ lockfile = File.expand_path("Gemfile.lock", __dir__)
16
+ File.foreach(lockfile, chomp: true).find { _1[/tag: (.*)$/] }
17
+ Regexp.last_match(1)
13
18
  end
14
19
 
15
20
  def gemspec_requirement
@@ -43,9 +48,13 @@ group :development, :test do
43
48
  # a specific rails codebase.
44
49
  gem "rails", github: "rails/rails", tag: RailsTag.call
45
50
 
51
+ # Needed for the test suite
52
+ gem "msgpack", ">= 1.7.0"
53
+
46
54
  gem "rake"
47
- gem "byebug"
55
+ gem "debug"
48
56
  gem "minitest-excludes", "~> 2.0.1"
57
+ gem "minitest-github_action_reporter", github: "BuonOmo/minitest-github_action_reporter", require: "minitest/github_action_reporter_plugin"
49
58
 
50
59
  # Gems used by the ActiveRecord test suite
51
60
  gem "bcrypt", "~> 3.1.18"
@@ -14,7 +14,7 @@ Gem::Specification.new do |spec|
14
14
  spec.description = "Allows the use of CockroachDB as a backend for ActiveRecord and Rails apps."
15
15
  spec.homepage = "https://github.com/cockroachdb/activerecord-cockroachdb-adapter"
16
16
 
17
- spec.add_dependency "activerecord", "~> 7.0.3"
17
+ spec.add_dependency "activerecord", "~> 7.1.0"
18
18
  spec.add_dependency "pg", "~> 1.2"
19
19
  spec.add_dependency "rgeo-activerecord", "~> 7.0.0"
20
20
 
data/bin/console CHANGED
@@ -12,39 +12,23 @@ require "active_record"
12
12
  # structure_load(Post.connection_db_config, "awesome-file.sql")
13
13
  require "active_record/connection_adapters/cockroachdb/database_tasks"
14
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
15
+ schema_kind = ENV.fetch("SCHEMA_KIND", "default")
16
+
17
+ system("cockroach sql --insecure --host=localhost:26257 --execute='drop database if exists ar_crdb_console'",
18
+ exception: true)
19
+ system("cockroach sql --insecure --host=localhost:26257 --execute='create database ar_crdb_console'",
20
+ exception: true)
21
+
22
+ ActiveRecord::Base.establish_connection(
23
+ #Alternative version: "cockroachdb://root@localhost:26257/ar_crdb_console"
24
+ adapter: "cockroachdb",
25
+ host: "localhost",
26
+ port: 26257,
27
+ user: "root",
28
+ database: "ar_crdb_console"
29
+ )
30
+
31
+ load "#{__dir__}/console_schemas/#{schema_kind}.rb"
48
32
 
49
33
  require "irb"
50
34
  IRB.start(__FILE__)
@@ -0,0 +1,9 @@
1
+ class Post < ActiveRecord::Base
2
+ end
3
+
4
+ ActiveRecord::Schema.define do
5
+ create_table("posts") do |t|
6
+ t.string :title
7
+ t.text :body
8
+ end
9
+ end
@@ -0,0 +1,23 @@
1
+ class Post < ActiveRecord::Base
2
+ self.table_name = "bar.posts"
3
+ end
4
+
5
+ class Comment < ActiveRecord::Base
6
+ self.table_name = "foo.comments"
7
+ end
8
+
9
+ ActiveRecord::Schema.define do
10
+ create_schema("foo")
11
+ create_schema("bar")
12
+ create_table("bar.posts") do |t|
13
+ t.string :title
14
+ t.text :body
15
+ end
16
+
17
+ create_table("foo.comments") do |t|
18
+ t.integer :post_id
19
+ t.text :body
20
+ end
21
+
22
+ add_foreign_key "foo.comments", "bar.posts", column: "post_id"
23
+ end
@@ -9,6 +9,7 @@ pid_file="$root_dir/tmp/cockroach.pid"
9
9
  log_file="$root_dir/tmp/cockroachdb.log"
10
10
 
11
11
  mkdir -p "$root_dir/tmp"
12
+ [[ -f "$pid_file" ]] && kill -9 $(cat "$pid_file")
12
13
  rm -f "$pid_file"
13
14
 
14
15
  if ! (( ${+commands[cockroach]} )); then
@@ -20,8 +21,6 @@ cockroach start-single-node \
20
21
  --insecure --store=type=mem,size=0.25 --advertise-addr=localhost --pid-file "$pid_file" \
21
22
  &> "$log_file" &
22
23
 
23
- cockroach_pid=$!
24
-
25
24
  until [[ -f "$pid_file" ]]; do
26
25
  sleep 1
27
26
  done
@@ -41,8 +40,9 @@ ALTER DATABASE system CONFIGURE ZONE USING "gc.ttlseconds" = 600;
41
40
 
42
41
  CREATE DATABASE activerecord_unittest;
43
42
  CREATE DATABASE activerecord_unittest2;
44
- SQL
45
43
 
46
- tail -f "$log_file"
44
+ SET CLUSTER SETTING sql.defaults.experimental_alter_column_type.enabled = 'true';
45
+ SET CLUSTER SETTING sql.defaults.experimental_temporary_tables.enabled = 'true';
46
+ SQL
47
47
 
48
- trap "kill $cockroach_pid" EXIT
48
+ echo "CockroachDB started. PID: $(cat "$pid_file"). log: $log_file"
data/build/Dockerfile CHANGED
@@ -1,7 +1,7 @@
1
1
  # This Dockerfile extends the Examples-ORM testing image in order to
2
2
  # install specific dependencies required for ActiveRecord tests.
3
3
 
4
- FROM cockroachdb/example-orms-builder:20200413-1918
4
+ FROM us-east1-docker.pkg.dev/crl-ci-images/cockroach/example-orms-builder:20200413-1918
5
5
 
6
6
  # Native dependencies for libxml-ruby and sqlite3.
7
7
  RUN apt-get --allow-releaseinfo-change update -y && apt-get install -y \
@@ -22,6 +22,14 @@ module Arel # :nodoc:
22
22
  module Visitors # :nodoc:
23
23
  class CockroachDB < PostgreSQL # :nodoc:
24
24
  include RGeo::ActiveRecord::SpatialToSql
25
+
26
+ def visit_Arel_Nodes_JoinSource(o, collector)
27
+ super
28
+ if o.aost
29
+ collector << " AS OF SYSTEM TIME '#{o.aost.iso8601}'"
30
+ end
31
+ collector
32
+ end
25
33
  end
26
34
  end
27
35
  end
@@ -1,11 +1,11 @@
1
1
  module ActiveRecord
2
2
  module ConnectionAdapters
3
3
  module CockroachDB
4
- class Column < PostgreSQLColumn
4
+ class Column < PostgreSQL::Column
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,
8
- default_function = nil, collation: nil, comment: nil,
8
+ default_function = nil, collation: nil, comment: nil, identity: nil,
9
9
  serial: nil, spatial: nil, generated: nil, hidden: nil)
10
10
  @sql_type_metadata = sql_type_metadata
11
11
  @geographic = !!(sql_type_metadata.sql_type =~ /geography\(/i)
@@ -30,7 +30,7 @@ module ActiveRecord
30
30
  build_from_sql_type(sql_type_metadata.sql_type)
31
31
  end
32
32
  super(name, default, sql_type_metadata, null, default_function,
33
- collation: collation, comment: comment, serial: serial, generated: generated)
33
+ collation: collation, comment: comment, serial: serial, generated: generated, identity: identity)
34
34
  if spatial? && @srid
35
35
  @limit = { srid: @srid, type: to_type_name(geometric_type) }
36
36
  @limit[:has_z] = true if @has_z
@@ -53,10 +53,6 @@ module ActiveRecord
53
53
  spatial? ? @limit : super
54
54
  end
55
55
 
56
- def virtual?
57
- @generated.present?
58
- end
59
-
60
56
  def hidden?
61
57
  @hidden
62
58
  end
@@ -45,6 +45,14 @@ module ActiveRecord
45
45
  def st_polygon(name, options = {})
46
46
  column(name, :st_polygon, **options)
47
47
  end
48
+
49
+ private
50
+
51
+ def valid_column_definition_options
52
+ spatial = [:srid, :has_z, :has_m, :geographic, :spatial_type]
53
+ crdb = [:hidden]
54
+ super + spatial + crdb
55
+ end
48
56
  end
49
57
  end
50
58
 
@@ -2,21 +2,6 @@ module ActiveRecord
2
2
  module ConnectionAdapters
3
3
  module CockroachDB
4
4
  module DatabaseStatements
5
- # Since CockroachDB will run all transactions with serializable isolation,
6
- # READ UNCOMMITTED, READ COMMITTED, and REPEATABLE READ are all aliases
7
- # for SERIALIZABLE. This lets the adapter support all isolation levels,
8
- # but READ UNCOMMITTED has been removed from this list because the
9
- # ActiveRecord transaction isolation test fails for READ UNCOMMITTED.
10
- # See https://www.cockroachlabs.com/docs/v19.2/transactions.html#isolation-levels
11
- def transaction_isolation_levels
12
- {
13
- read_committed: "READ COMMITTED",
14
- repeatable_read: "REPEATABLE READ",
15
- serializable: "SERIALIZABLE",
16
- read_uncommitted: "READ UNCOMMITTED"
17
- }
18
- end
19
-
20
5
  # Overridden to avoid using transactions for schema creation.
21
6
  def insert_fixtures_set(fixture_set, tables_to_delete = [])
22
7
  fixture_inserts = build_fixture_statements(fixture_set)
@@ -29,74 +14,6 @@ module ActiveRecord
29
14
  end
30
15
  end
31
16
  end
32
-
33
- private
34
- def execute_batch(statements, name = nil)
35
- statements.each do |statement|
36
- execute(statement, name)
37
- end
38
- end
39
-
40
- DEFAULT_INSERT_VALUE = Arel.sql("DEFAULT").freeze
41
- private_constant :DEFAULT_INSERT_VALUE
42
-
43
- def default_insert_value(column)
44
- DEFAULT_INSERT_VALUE
45
- end
46
-
47
- def build_fixture_sql(fixtures, table_name)
48
- columns = schema_cache.columns_hash(table_name)
49
-
50
- values_list = fixtures.map do |fixture|
51
- fixture = fixture.stringify_keys
52
-
53
- unknown_columns = fixture.keys - columns.keys
54
- if unknown_columns.any?
55
- raise Fixture::FixtureError, %(table "#{table_name}" has no columns named #{unknown_columns.map(&:inspect).join(', ')}.)
56
- end
57
-
58
- columns.map do |name, column|
59
- if fixture.key?(name)
60
- type = lookup_cast_type_from_column(column)
61
- with_yaml_fallback(type.serialize(fixture[name]))
62
- else
63
- default_insert_value(column)
64
- end
65
- end
66
- end
67
-
68
- table = Arel::Table.new(table_name)
69
- manager = Arel::InsertManager.new
70
- manager.into(table)
71
-
72
- if values_list.size == 1
73
- values = values_list.shift
74
- new_values = []
75
- columns.each_key.with_index { |column, i|
76
- unless values[i].equal?(DEFAULT_INSERT_VALUE)
77
- new_values << values[i]
78
- manager.columns << table[column]
79
- end
80
- }
81
- values_list << new_values
82
- else
83
- columns.each_key { |column| manager.columns << table[column] }
84
- end
85
-
86
- manager.values = manager.create_values_list(values_list)
87
- manager.to_sql
88
- end
89
-
90
- def build_fixture_statements(fixture_set)
91
- fixture_set.map do |table_name, fixtures|
92
- next if fixtures.empty?
93
- build_fixture_sql(fixtures, table_name)
94
- end.compact
95
- end
96
-
97
- def with_multi_statements
98
- yield
99
- end
100
17
  end
101
18
  end
102
19
  end
@@ -10,37 +10,44 @@ module ActiveRecord
10
10
  "https://github.com/cockroachdb/activerecord-cockroachdb-adapter/issues/new"
11
11
  end
12
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."
13
+ # "See https://github.com/cockroachdb/cockroach/issues/26443."
14
+ search_path =
15
+ case ActiveRecord.dump_schemas
16
+ when :schema_search_path
17
+ configuration_hash[:schema_search_path]
18
+ when :all
19
+ nil
20
+ when String
21
+ ActiveRecord.dump_schemas
21
22
  end
22
- end
23
23
 
24
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
25
+ begin
26
+ old_search_path = conn.schema_search_path
27
+ conn.schema_search_path = search_path
28
+ File.open(filename, "w") do |file|
29
+ %w(SCHEMAS TYPES).each do |object_kind|
30
+ ActiveRecord::Base.connection.execute("SHOW CREATE ALL #{object_kind}").each_row { file.puts _1 }
31
+ end
29
32
 
30
- ignore_tables = ActiveRecord::SchemaDumper.ignore_tables.to_set
33
+ ignore_tables = ActiveRecord::SchemaDumper.ignore_tables.to_set
31
34
 
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
35
+ conn.execute("SHOW CREATE ALL TABLES").each_row do |(sql)|
36
+ if sql.start_with?("CREATE")
37
+ table_name = sql[/CREATE TABLE (?:.*?\.)?\"?(.*?)[\" ]/, 1]
38
+ next if ignore_tables.member?(table_name)
39
+ elsif sql.start_with?("ALTER")
40
+ table_name = sql[/ALTER TABLE (?:.*?\.)?\"?(.*?)[\" ]/, 1]
41
+ ref_table_name = sql[/REFERENCES (?:.*?\.)?\"?(.*?)[\" ]/, 1]
42
+ next if ignore_tables.member?(table_name) || ignore_tables.member?(ref_table_name)
43
+ end
41
44
 
42
- file.puts sql
45
+ file.puts sql
46
+ end
47
+ file.puts "SET seach_path TO #{conn.schema_search_path};\n\n"
43
48
  end
49
+ ensure
50
+ conn.schema_search_path = old_search_path
44
51
  end
45
52
  end
46
53
 
@@ -11,6 +11,14 @@ module ActiveRecord
11
11
  module ConnectionAdapters
12
12
  module CockroachDB
13
13
  module ReferentialIntegrity
14
+ # CockroachDB will raise a `PG::ForeignKeyViolation` when re-enabling
15
+ # referential integrity (e.g: adding a foreign key with invalid data
16
+ # raises).
17
+ # So foreign keys should always be valid for that matter.
18
+ def check_all_foreign_keys_valid!
19
+ true
20
+ end
21
+
14
22
  def disable_referential_integrity
15
23
  foreign_keys = tables.map { |table| foreign_keys(table) }.flatten
16
24
 
@@ -31,16 +39,12 @@ module ActiveRecord
31
39
 
32
40
  begin
33
41
  foreign_keys.each do |foreign_key|
34
- begin
35
- add_foreign_key(foreign_key.from_table, foreign_key.to_table, **foreign_key.options)
36
- rescue ActiveRecord::StatementInvalid => error
37
- if error.cause.class == PG::DuplicateObject
38
- # This error is safe to ignore because the yielded caller
39
- # already re-added the foreign key constraint.
40
- else
41
- raise error
42
- end
43
- end
42
+ # Avoid having PG:DuplicateObject error if a test is ran in transaction.
43
+ # TODO: verify that there is no cache issue related to running this (e.g: fk
44
+ # still in cache but not in db)
45
+ next if foreign_key_exists?(foreign_key.from_table, name: foreign_key.options[:name])
46
+
47
+ add_foreign_key(foreign_key.from_table, foreign_key.to_table, **foreign_key.options)
44
48
  end
45
49
  ensure
46
50
  ActiveRecord::Base.table_name_prefix = old_prefix
@@ -36,16 +36,25 @@ module ActiveRecord
36
36
  # Modified version of the postgresql foreign_keys method.
37
37
  # Replaces t2.oid::regclass::text with t2.relname since this is
38
38
  # more efficient in CockroachDB.
39
+ # Also, CockroachDB does not append the schema name in relname,
40
+ # so we append it manually.
39
41
  def foreign_keys(table_name)
40
42
  scope = quoted_scope(table_name)
41
43
  fk_info = exec_query(<<~SQL, "SCHEMA")
42
- SELECT t2.relname AS to_table, a1.attname AS column, a2.attname AS primary_key, c.conname AS name, c.confupdtype AS on_update, c.confdeltype AS on_delete, c.convalidated AS valid
44
+ SELECT CASE
45
+ WHEN n2.nspname = current_schema()
46
+ THEN ''
47
+ ELSE n2.nspname || '.'
48
+ END || t2.relname AS to_table,
49
+ a1.attname AS column, a2.attname AS primary_key, c.conname AS name, c.confupdtype AS on_update, c.confdeltype AS on_delete, c.convalidated AS valid, c.condeferrable AS deferrable, c.condeferred AS deferred,
50
+ c.conkey, c.confkey, c.conrelid, c.confrelid
43
51
  FROM pg_constraint c
44
52
  JOIN pg_class t1 ON c.conrelid = t1.oid
45
53
  JOIN pg_class t2 ON c.confrelid = t2.oid
46
54
  JOIN pg_attribute a1 ON a1.attnum = c.conkey[1] AND a1.attrelid = t1.oid
47
55
  JOIN pg_attribute a2 ON a2.attnum = c.confkey[1] AND a2.attrelid = t2.oid
48
56
  JOIN pg_namespace t3 ON c.connamespace = t3.oid
57
+ JOIN pg_namespace n2 ON t2.relnamespace = n2.oid
49
58
  WHERE c.contype = 'f'
50
59
  AND t1.relname = #{scope[:name]}
51
60
  AND t3.nspname = #{scope[:schema]}
@@ -53,17 +62,31 @@ module ActiveRecord
53
62
  SQL
54
63
 
55
64
  fk_info.map do |row|
65
+ to_table = PostgreSQL::Utils.unquote_identifier(row["to_table"])
66
+ conkey = row["conkey"].scan(/\d+/).map(&:to_i)
67
+ confkey = row["confkey"].scan(/\d+/).map(&:to_i)
68
+
69
+ if conkey.size > 1
70
+ column = column_names_from_column_numbers(row["conrelid"], conkey)
71
+ primary_key = column_names_from_column_numbers(row["confrelid"], confkey)
72
+ else
73
+ column = PostgreSQL::Utils.unquote_identifier(row["column"])
74
+ primary_key = row["primary_key"]
75
+ end
76
+
56
77
  options = {
57
- column: row["column"],
78
+ column: column,
58
79
  name: row["name"],
59
- primary_key: row["primary_key"]
80
+ primary_key: primary_key
60
81
  }
61
-
62
82
  options[:on_delete] = extract_foreign_key_action(row["on_delete"])
63
83
  options[:on_update] = extract_foreign_key_action(row["on_update"])
84
+ options[:deferrable] = extract_constraint_deferrable(row["deferrable"], row["deferred"])
85
+
64
86
  options[:validate] = row["valid"]
87
+ to_table = PostgreSQL::Utils.unquote_identifier(row["to_table"])
65
88
 
66
- ForeignKeyDefinition.new(table_name, row["to_table"], options)
89
+ ForeignKeyDefinition.new(table_name, to_table, options)
67
90
  end
68
91
  end
69
92
 
@@ -76,16 +99,20 @@ module ActiveRecord
76
99
 
77
100
  # override
78
101
  # https://github.com/rails/rails/blob/6-0-stable/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb#L624
79
- def new_column_from_field(table_name, field)
80
- column_name, type, default, notnull, oid, fmod, collation, comment, generated, hidden = field
102
+ def new_column_from_field(table_name, field, _definition)
103
+ column_name, type, default, notnull, oid, fmod, collation, comment, identity, attgenerated, hidden = field
81
104
  type_metadata = fetch_type_metadata(column_name, type, oid.to_i, fmod.to_i)
82
105
  default_value = extract_value_from_default(default)
83
- default_function = extract_default_function(default_value, default)
84
106
 
85
- serial =
86
- if (match = default_function&.match(/\Anextval\('"?(?<sequence_name>.+_(?<suffix>seq\d*))"?'::regclass\)\z/))
87
- sequence_name_from_parts(table_name, column_name, match[:suffix]) == match[:sequence_name]
88
- end
107
+ if attgenerated.present?
108
+ default_function = default
109
+ else
110
+ default_function = extract_default_function(default_value, default)
111
+ end
112
+
113
+ if match = default_function&.match(/\Anextval\('"?(?<sequence_name>.+_(?<suffix>seq\d*))"?'::regclass\)\z/)
114
+ serial = sequence_name_from_parts(table_name, column_name, match[:suffix]) == match[:sequence_name]
115
+ end
89
116
 
90
117
  # {:dimension=>2, :has_m=>false, :has_z=>false, :name=>"latlon", :srid=>0, :type=>"GEOMETRY"}
91
118
  spatial = spatial_column_info(table_name).get(column_name, type_metadata.sql_type)
@@ -99,8 +126,9 @@ module ActiveRecord
99
126
  collation: collation,
100
127
  comment: comment.presence,
101
128
  serial: serial,
129
+ identity: identity.presence,
102
130
  spatial: spatial,
103
- generated: generated,
131
+ generated: attgenerated,
104
132
  hidden: hidden
105
133
  )
106
134
  end
@@ -1,15 +1,16 @@
1
1
  module ActiveRecord
2
2
  module Type
3
- class << self
3
+ module CRDBExt
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
7
  def adapter_name_from(model)
8
- name = model.connection_db_config.adapter.to_sym
8
+ name = super
9
9
  return :postgresql if name == :cockroachdb
10
10
 
11
11
  name
12
12
  end
13
13
  end
14
+ singleton_class.prepend CRDBExt
14
15
  end
15
16
  end
@@ -1,5 +1,6 @@
1
1
  require "rgeo/active_record"
2
2
 
3
+ require_relative "../../arel/nodes/join_source_ext"
3
4
  require "active_record/connection_adapters/postgresql_adapter"
4
5
  require "active_record/connection_adapters/cockroachdb/attribute_methods"
5
6
  require "active_record/connection_adapters/cockroachdb/column_methods"
@@ -160,7 +161,7 @@ module ActiveRecord
160
161
  end
161
162
 
162
163
  def supports_bulk_alter?
163
- false
164
+ true
164
165
  end
165
166
 
166
167
  def supports_json?
@@ -181,7 +182,15 @@ module ActiveRecord
181
182
  end
182
183
 
183
184
  def supports_partial_index?
184
- @crdb_version >= 2020
185
+ true
186
+ end
187
+
188
+ def supports_index_include?
189
+ false
190
+ end
191
+
192
+ def supports_exclusion_constraints?
193
+ false
185
194
  end
186
195
 
187
196
  def supports_expression_index?
@@ -196,7 +205,7 @@ module ActiveRecord
196
205
  end
197
206
 
198
207
  def supports_comments?
199
- @crdb_version >= 2010
208
+ true
200
209
  end
201
210
 
202
211
  def supports_comments_in_create?
@@ -208,11 +217,11 @@ module ActiveRecord
208
217
  end
209
218
 
210
219
  def supports_virtual_columns?
211
- @crdb_version >= 2110
220
+ true
212
221
  end
213
222
 
214
223
  def supports_string_to_array_coercion?
215
- @crdb_version >= 2020
224
+ true
216
225
  end
217
226
 
218
227
  def supports_partitioned_indexes?
@@ -238,62 +247,30 @@ module ActiveRecord
238
247
  alias index_name_length max_identifier_length
239
248
  alias table_alias_length max_identifier_length
240
249
 
241
- def initialize(connection, logger, conn_params, config)
242
- super(connection, logger, conn_params, config)
243
-
244
- # crdb_version is the version of the binary running on the node. We
245
- # really want to use `SHOW CLUSTER SETTING version` to get the cluster
246
- # version, but that is only available to admins. Instead, we can use
247
- # crdb_internal.is_at_least_version, but that's only available in 22.1.
248
- crdb_version_string = query_value("SHOW crdb_version")
249
- if crdb_version_string.include? "v22.1"
250
- version_num = query_value(<<~SQL, "VERSION")
251
- SELECT
252
- CASE
253
- WHEN crdb_internal.is_at_least_version('22.2') THEN 2220
254
- WHEN crdb_internal.is_at_least_version('22.1') THEN 2210
255
- ELSE 2120
256
- END;
257
- SQL
258
- else
259
- # This branch can be removed once the dialect stops supporting v21.2
260
- # and earlier.
261
- if crdb_version_string.include? "v1."
262
- version_num = 1
263
- elsif crdb_version_string.include? "v2."
264
- version_num 2
265
- elsif crdb_version_string.include? "v19.1."
266
- version_num = 1910
267
- elsif crdb_version_string.include? "v19.2."
268
- version_num = 1920
269
- elsif crdb_version_string.include? "v20.1."
270
- version_num = 2010
271
- elsif crdb_version_string.include? "v20.2."
272
- version_num = 2020
273
- elsif crdb_version_string.include? "v21.1."
274
- version_num = 2110
275
- else
276
- version_num = 2120
277
- end
278
- end
279
- @crdb_version = version_num.to_i
280
-
281
- # NOTE: this is normally in configure_connection, but that is run
282
- # before crdb_version is determined. Once all supported versions
283
- # of CockroachDB support SET intervalstyle it can safely be moved
284
- # back.
285
- # Set interval output format to ISO 8601 for ease of parsing by ActiveSupport::Duration.parse
286
- if @crdb_version >= 2120
287
- begin
288
- execute("SET intervalstyle_enabled = true", "SCHEMA")
289
- execute("SET intervalstyle = iso_8601", "SCHEMA")
290
- rescue
291
- # Ignore any error. This can happen with a cluster that has
292
- # not yet finalized the v21.2 upgrade. v21.2 does not have
293
- # a way to tell if the upgrade was finalized (see comment above).
294
- end
295
- end
296
- end
250
+ # NOTE: This commented bit of code allows to have access to crdb version,
251
+ # which can be useful for feature detection. However, we currently don't
252
+ # need, hence we avoid the extra queries.
253
+ #
254
+ # def initialize(connection, logger, conn_params, config)
255
+ # super(connection, logger, conn_params, config)
256
+
257
+ # # crdb_version is the version of the binary running on the node. We
258
+ # # really want to use `SHOW CLUSTER SETTING version` to get the cluster
259
+ # # version, but that is only available to admins. Instead, we can use
260
+ # # crdb_internal.is_at_least_version, but that's only available in 22.1.
261
+ # crdb_version_string = query_value("SHOW crdb_version")
262
+ # if crdb_version_string.include? "v22.1"
263
+ # version_num = query_value(<<~SQL, "VERSION")
264
+ # SELECT
265
+ # CASE
266
+ # WHEN crdb_internal.is_at_least_version('22.2') THEN 2220
267
+ # WHEN crdb_internal.is_at_least_version('22.1') THEN 2210
268
+ # ELSE 2120
269
+ # END;
270
+ # SQL
271
+ # end
272
+ # @crdb_version = version_num.to_i
273
+ # end
297
274
 
298
275
  def self.database_exists?(config)
299
276
  !!ActiveRecord::Base.cockroachdb_connection(config)
@@ -306,12 +283,12 @@ module ActiveRecord
306
283
  # (DO $$) that CockroachDB does not support.
307
284
  #
308
285
  # Given a name and an array of values, creates an enum type.
309
- def create_enum(name, values)
310
- sql_values = values.map { |s| "'#{s}'" }.join(", ")
286
+ def create_enum(name, values, **options)
287
+ sql_values = values.map { |s| quote(s) }.join(", ")
311
288
  query = <<~SQL
312
- CREATE TYPE IF NOT EXISTS \"#{name}\" AS ENUM (#{sql_values});
289
+ CREATE TYPE IF NOT EXISTS #{quote_table_name(name)} AS ENUM (#{sql_values});
313
290
  SQL
314
- exec_query(query)
291
+ internal_exec_query(query).tap { reload_type_map }
315
292
  end
316
293
 
317
294
  class << self
@@ -369,56 +346,6 @@ module ActiveRecord
369
346
 
370
347
  private
371
348
 
372
- # Configures the encoding, verbosity, schema search path, and time zone of the connection.
373
- # This is called by #connect and should not be called manually.
374
- #
375
- # NOTE(joey): This was cradled from postgresql_adapter.rb. This
376
- # was due to needing to override configuration statements.
377
- def configure_connection
378
- if @config[:encoding]
379
- @connection.set_client_encoding(@config[:encoding])
380
- end
381
- self.client_min_messages = @config[:min_messages] || "warning"
382
- self.schema_search_path = @config[:schema_search_path] || @config[:schema_order]
383
-
384
- # Use standard-conforming strings so we don't have to do the E'...' dance.
385
- set_standard_conforming_strings
386
-
387
- variables = @config.fetch(:variables, {}).stringify_keys
388
-
389
- # If using Active Record's time zone support configure the connection to return
390
- # TIMESTAMP WITH ZONE types in UTC.
391
- unless variables["timezone"]
392
- if ActiveRecord.default_timezone == :utc
393
- variables["timezone"] = "UTC"
394
- elsif @local_tz
395
- variables["timezone"] = @local_tz
396
- end
397
- end
398
-
399
- # NOTE(joey): This is a workaround as CockroachDB 1.1.x
400
- # supports SET TIME ZONE <...> and SET "time zone" = <...> but
401
- # not SET timezone = <...>.
402
- if variables.key?("timezone")
403
- tz = variables.delete("timezone")
404
- execute("SET TIME ZONE #{quote(tz)}", "SCHEMA")
405
- end
406
-
407
- # SET statements from :variables config hash
408
- # https://www.postgresql.org/docs/current/static/sql-set.html
409
- variables.map do |k, v|
410
- if v == ":default" || v == :default
411
- # Sets the value to the global or compile default
412
-
413
- # NOTE(joey): I am not sure if simply commenting this out
414
- # is technically correct.
415
- # execute("SET #{k} = DEFAULT", "SCHEMA")
416
- elsif !v.nil?
417
- execute("SET SESSION #{k} = #{quote(v)}", "SCHEMA")
418
- end
419
- end
420
- end
421
-
422
349
  # Override extract_value_from_default because the upstream definition
423
350
  # doesn't handle the variations in CockroachDB's behavior.
424
351
  def extract_value_from_default(default)
@@ -502,7 +429,8 @@ module ActiveRecord
502
429
  SELECT a.attname, format_type(a.atttypid, a.atttypmod),
503
430
  pg_get_expr(d.adbin, d.adrelid), a.attnotnull, a.atttypid, a.atttypmod,
504
431
  c.collname, NULL AS comment,
505
- #{supports_virtual_columns? ? 'attgenerated' : quote('')} as attgenerated,
432
+ attidentity,
433
+ attgenerated,
506
434
  NULL as is_hidden
507
435
  FROM pg_attribute a
508
436
  LEFT JOIN pg_attrdef d ON a.attrelid = d.adrelid AND a.attnum = d.adnum
@@ -517,26 +445,31 @@ module ActiveRecord
517
445
 
518
446
  # Use regex comparison because if a type is an array it will
519
447
  # have [] appended to the end of it.
520
- target_types = [
521
- /geometry/,
522
- /geography/,
523
- /interval/,
524
- /numeric/
525
- ]
526
-
527
- re = Regexp.union(target_types)
448
+ re = /\A(?:geometry|geography|interval|numeric)/
449
+
450
+ # 0: attname
451
+ # 1: type
452
+ # 2: default
453
+ # 3: attnotnull
454
+ # 4: atttypid
455
+ # 5: atttypmod
456
+ # 6: collname
457
+ # 7: comment
458
+ # 8: attidentity
459
+ # 9: attgenerated
460
+ # 10: is_hidden
528
461
  fields.map do |field|
529
462
  dtype = field[1]
530
463
  field[1] = crdb_fields[field[0]][2].downcase if re.match(dtype)
531
464
  field[7] = crdb_fields[field[0]][1]&.gsub!(/^\'|\'?$/, '')
532
- field[9] = true if crdb_fields[field[0]][3]
465
+ field[10] = true if crdb_fields[field[0]][3]
533
466
  field
534
467
  end
535
468
  fields.delete_if do |field|
536
469
  # Don't include rowid column if it is hidden and the primary key
537
470
  # is not defined (meaning CRDB implicitly created it).
538
471
  if field[0] == CockroachDBAdapter::DEFAULT_PRIMARY_KEY
539
- field[9] && !primary_key(table_name)
472
+ field[10] && !primary_key(table_name)
540
473
  else
541
474
  false # Keep this entry.
542
475
  end
@@ -549,11 +482,14 @@ module ActiveRecord
549
482
  # precision, and scale information in the type.
550
483
  # Ex. geometry -> geometry(point, 4326)
551
484
  def crdb_column_definitions(table_name)
485
+ table_name = PostgreSQL::Utils.extract_schema_qualified_name(table_name)
486
+ table = table_name.identifier
487
+ with_schema = " AND c.table_schema = #{quote(table_name.schema)}" if table_name.schema
552
488
  fields = \
553
489
  query(<<~SQL, "SCHEMA")
554
490
  SELECT c.column_name, c.column_comment, c.crdb_sql_type, c.is_hidden::BOOLEAN
555
- FROM information_schema.columns c
556
- WHERE c.table_name = #{quote(table_name)}
491
+ FROM information_schema.columns c
492
+ WHERE c.table_name = #{quote(table)}#{with_schema}
557
493
  SQL
558
494
 
559
495
  fields.reduce({}) do |a, e|
@@ -594,21 +530,10 @@ module ActiveRecord
594
530
  def load_additional_types(oids = nil)
595
531
  if @config[:use_follower_reads_for_type_introspection]
596
532
  initializer = OID::TypeMapInitializer.new(type_map)
597
-
598
- query = <<~SQL
599
- SELECT t.oid, t.typname, t.typelem, t.typdelim, t.typinput, r.rngsubtype, t.typtype, t.typbasetype
600
- FROM pg_type as t
601
- LEFT JOIN pg_range as r ON oid = rngtypid AS OF SYSTEM TIME '-10s'
602
- SQL
603
-
604
- if oids
605
- query += "WHERE t.oid IN (%s)" % oids.join(", ")
606
- else
607
- query += initializer.query_conditions_for_initial_load
608
- end
609
-
610
- execute_and_clear(query, "SCHEMA", []) do |records|
611
- initializer.run(records)
533
+ load_types_queries_with_aost(initializer, oids) do |query|
534
+ execute_and_clear(query, "SCHEMA", [], allow_retry: true, materialize_transactions: false) do |records|
535
+ initializer.run(records)
536
+ end
612
537
  end
613
538
  else
614
539
  super
@@ -619,6 +544,21 @@ module ActiveRecord
619
544
  super
620
545
  end
621
546
 
547
+ def load_types_queries_with_aost(initializer, oids)
548
+ query = <<~SQL
549
+ SELECT t.oid, t.typname, t.typelem, t.typdelim, t.typinput, r.rngsubtype, t.typtype, t.typbasetype
550
+ FROM pg_type as t
551
+ LEFT JOIN pg_range as r ON oid = rngtypid AS OF SYSTEM TIME '-10s'
552
+ SQL
553
+ if oids
554
+ yield query + "WHERE t.oid IN (%s)" % oids.join(", ")
555
+ else
556
+ yield query + initializer.query_conditions_for_known_type_names
557
+ yield query + initializer.query_conditions_for_known_type_types
558
+ yield query + initializer.query_conditions_for_array_types
559
+ end
560
+ end
561
+
622
562
  # override
623
563
  # This method maps data types to their proper decoder.
624
564
  #
@@ -649,15 +589,13 @@ module ActiveRecord
649
589
  WHERE t.typname IN (%s)
650
590
  SQL
651
591
 
652
- coders = execute_and_clear(query, "SCHEMA", []) do |result|
653
- result
654
- .map { |row| construct_coder(row, coders_by_name[row["typname"]]) }
655
- .compact
592
+ coders = execute_and_clear(query, "SCHEMA", [], allow_retry: true, materialize_transactions: false) do |result|
593
+ result.filter_map { |row| construct_coder(row, coders_by_name[row["typname"]]) }
656
594
  end
657
595
 
658
596
  map = PG::TypeMapByOid.new
659
597
  coders.each { |coder| map.add_coder(coder) }
660
- @connection.type_map_for_results = map
598
+ @raw_connection.type_map_for_results = map
661
599
 
662
600
  @type_map_for_results = PG::TypeMapByOid.new
663
601
  @type_map_for_results.default_type_map = map
@@ -3,6 +3,23 @@
3
3
  module ActiveRecord
4
4
  class Relation
5
5
  module QueryMethodsExt
6
+ def aost!(time) # :nodoc:
7
+ unless time.nil? || time.is_a?(Time)
8
+ raise ArgumentError, "Unsupported argument type: #{time} (#{time.class})"
9
+ end
10
+
11
+ @aost = time
12
+ self
13
+ end
14
+
15
+ # Set system time for the current query. Using
16
+ # `.aost(nil)` resets.
17
+ #
18
+ # See cockroachlabs.com/docs/stable/as-of-system-time
19
+ def aost(time)
20
+ spawn.aost!(time)
21
+ end
22
+
6
23
  def from!(...) # :nodoc:
7
24
  @force_index = nil
8
25
  @index_hint = nil
@@ -59,8 +76,20 @@ module ActiveRecord
59
76
  self
60
77
  end
61
78
 
79
+ # TODO: reset or no reset?
80
+
81
+ def show_create
82
+ connection.execute("show create table #{connection.quote_table_name self.table_name}").first["create_statement"]
83
+ end
84
+
62
85
  private
63
86
 
87
+ def build_arel(...)
88
+ arel = super
89
+ arel.aost(@aost) if @aost.present?
90
+ arel
91
+ end
92
+
64
93
  def from_clause_is_a_table_name?
65
94
  # if empty, we are just dealing with the current table.
66
95
  return true if from_clause.empty?
@@ -94,5 +123,5 @@ module ActiveRecord
94
123
  # as ancestor. That is how active_record is doing is as well.
95
124
  #
96
125
  # @see https://github.com/rails/rails/blob/914130a9f/activerecord/lib/active_record/querying.rb#L23
97
- Querying.delegate(:force_index, :index_hint, to: :all)
126
+ Querying.delegate(:force_index, :index_hint, :aost, :show_create, to: :all)
98
127
  end
@@ -0,0 +1,28 @@
1
+ module Arel
2
+ module Nodes
3
+ module JoinSourceExt
4
+ def initialize(...)
5
+ super
6
+ @aost = nil
7
+ end
8
+
9
+ def hash
10
+ [*super, aost].hash
11
+ end
12
+
13
+ def eql?(other)
14
+ super && aost == other.aost
15
+ end
16
+ alias_method :==, :eql?
17
+ end
18
+ JoinSource.attr_accessor :aost
19
+ JoinSource.prepend JoinSourceExt
20
+ end
21
+ module SelectManagerExt
22
+ def aost(time)
23
+ @ctx.source.aost = time
24
+ nil
25
+ end
26
+ end
27
+ SelectManager.prepend SelectManagerExt
28
+ end
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.3"
4
+ COCKROACH_DB_ADAPTER_VERSION = "7.1.0"
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.3
4
+ version: 7.1.0
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-08-23 00:00:00.000000000 Z
11
+ date: 2024-01-30 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: 7.0.3
19
+ version: 7.1.0
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: 7.0.3
26
+ version: 7.1.0
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: pg
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -74,6 +74,7 @@ executables: []
74
74
  extensions: []
75
75
  extra_rdoc_files: []
76
76
  files:
77
+ - ".editorconfig"
77
78
  - ".github/workflows/ci.yml"
78
79
  - ".github/workflows/docker.yml"
79
80
  - ".gitignore"
@@ -87,6 +88,8 @@ files:
87
88
  - Rakefile
88
89
  - activerecord-cockroachdb-adapter.gemspec
89
90
  - bin/console
91
+ - bin/console_schemas/default.rb
92
+ - bin/console_schemas/schemas.rb
90
93
  - bin/setup
91
94
  - bin/start-cockroachdb
92
95
  - build/Dockerfile
@@ -118,6 +121,7 @@ files:
118
121
  - lib/active_record/migration/cockroachdb/compatibility.rb
119
122
  - lib/active_record/relation/query_methods_ext.rb
120
123
  - lib/activerecord-cockroachdb-adapter.rb
124
+ - lib/arel/nodes/join_source_ext.rb
121
125
  - lib/version.rb
122
126
  homepage: https://github.com/cockroachdb/activerecord-cockroachdb-adapter
123
127
  licenses:
@@ -139,7 +143,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
139
143
  - !ruby/object:Gem::Version
140
144
  version: '0'
141
145
  requirements: []
142
- rubygems_version: 3.4.9
146
+ rubygems_version: 3.4.10
143
147
  signing_key:
144
148
  specification_version: 4
145
149
  summary: CockroachDB adapter for ActiveRecord.