activerecord-cockroachdb-adapter 7.0.3 → 7.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.editorconfig +7 -0
- data/.github/workflows/ci.yml +24 -12
- data/CHANGELOG.md +7 -0
- data/Gemfile +10 -1
- data/activerecord-cockroachdb-adapter.gemspec +1 -1
- data/bin/console +17 -33
- data/bin/console_schemas/default.rb +9 -0
- data/bin/console_schemas/schemas.rb +23 -0
- data/bin/start-cockroachdb +5 -5
- data/build/Dockerfile +1 -1
- data/lib/active_record/connection_adapters/cockroachdb/arel_tosql.rb +8 -0
- data/lib/active_record/connection_adapters/cockroachdb/column.rb +3 -7
- data/lib/active_record/connection_adapters/cockroachdb/column_methods.rb +8 -0
- data/lib/active_record/connection_adapters/cockroachdb/database_statements.rb +0 -83
- data/lib/active_record/connection_adapters/cockroachdb/database_tasks.rb +31 -24
- data/lib/active_record/connection_adapters/cockroachdb/referential_integrity.rb +14 -10
- data/lib/active_record/connection_adapters/cockroachdb/schema_statements.rb +41 -13
- data/lib/active_record/connection_adapters/cockroachdb/type.rb +3 -2
- data/lib/active_record/connection_adapters/cockroachdb_adapter.rb +86 -148
- data/lib/active_record/relation/query_methods_ext.rb +30 -1
- data/lib/arel/nodes/join_source_ext.rb +28 -0
- data/lib/version.rb +1 -1
- metadata +9 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6c3f85e82470685a2ece6851d557bcaf03f5a77ce309d76062c3960ca17d1606
|
4
|
+
data.tar.gz: b6ab7bfb9d29feebf217b70b11ccf2f73e6fb0853138cc27f4556f07b6c36dac
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 96fbb4c5a42c1ab3f2fe590f3c74012b883c818608c5b9d0525f30739f1d4d8733c95cfffd31a8a05350d6210d995f84d915727a494264d1cb1c217d10d200b1
|
7
|
+
data.tar.gz: 12ae86ab8f26eb074eabb60cd3f831eed44729d98f70bdc88d1c0d9c3d5cff5680ae7cf0a9643756395e6cfc1257cb7fc62ee5abab4c37c6f0fe45817fc619a0
|
data/.editorconfig
ADDED
data/.github/workflows/ci.yml
CHANGED
@@ -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
|
-
|
10
|
-
|
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:
|
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.
|
30
|
-
ruby: [
|
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@
|
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
|
-
|
41
|
-
|
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 "
|
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
|
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
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
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,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
|
data/bin/start-cockroachdb
CHANGED
@@ -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
|
-
|
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
|
-
|
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
|
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 <
|
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
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
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
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
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
|
-
|
33
|
+
ignore_tables = ActiveRecord::SchemaDumper.ignore_tables.to_set
|
31
34
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
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
|
-
|
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
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
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
|
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:
|
78
|
+
column: column,
|
58
79
|
name: row["name"],
|
59
|
-
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,
|
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,
|
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
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
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:
|
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
|
-
|
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 =
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
220
|
+
true
|
212
221
|
end
|
213
222
|
|
214
223
|
def supports_string_to_array_coercion?
|
215
|
-
|
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
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
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|
|
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
|
289
|
+
CREATE TYPE IF NOT EXISTS #{quote_table_name(name)} AS ENUM (#{sql_values});
|
313
290
|
SQL
|
314
|
-
|
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
|
-
|
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
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
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[
|
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[
|
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
|
-
|
556
|
-
WHERE c.table_name = #{quote(
|
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
|
-
|
599
|
-
|
600
|
-
|
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
|
-
@
|
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
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
|
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:
|
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
|
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
|
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.
|
146
|
+
rubygems_version: 3.4.10
|
143
147
|
signing_key:
|
144
148
|
specification_version: 4
|
145
149
|
summary: CockroachDB adapter for ActiveRecord.
|