activerecord-cockroachdb-adapter 8.0.3 → 8.1.1
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/CHANGELOG.md +10 -0
- data/CONTRIBUTING.md +10 -17
- data/Gemfile +2 -1
- data/Rakefile +0 -19
- data/lib/active_record/connection_adapters/cockroachdb/column.rb +69 -24
- data/lib/active_record/connection_adapters/cockroachdb/database_statements.rb +13 -3
- data/lib/active_record/connection_adapters/cockroachdb/database_tasks.rb +1 -1
- data/lib/active_record/connection_adapters/cockroachdb/oid/spatial.rb +34 -19
- data/lib/active_record/connection_adapters/cockroachdb/quoting.rb +19 -3
- data/lib/active_record/connection_adapters/cockroachdb/referential_integrity.rb +137 -51
- data/lib/active_record/connection_adapters/cockroachdb/schema_statements.rb +181 -102
- data/lib/active_record/connection_adapters/cockroachdb_adapter.rb +78 -34
- data/lib/version.rb +1 -1
- data/test/cases/adapter_test.rb +98 -0
- data/test/cases/adapters/cockroachdb/cached_plan_failure_test.rb +106 -0
- data/test/cases/adapters/cockroachdb/referential_integrity_test.rb +91 -0
- data/test/cases/adapters/postgresql/active_schema_test.rb +71 -0
- data/test/cases/adapters/postgresql/change_schema_test.rb +75 -0
- data/test/cases/adapters/postgresql/connection_test.rb +46 -0
- data/test/cases/adapters/postgresql/ddl_test.rb +326 -0
- data/test/cases/adapters/postgresql/interval_test.rb +131 -0
- data/test/cases/adapters/postgresql/nested_class_test.rb +22 -0
- data/test/cases/adapters/postgresql/numeric_test.rb +28 -0
- data/test/cases/adapters/postgresql/postgis_test.rb +199 -0
- data/test/cases/adapters/postgresql/postgresql_adapter_test.rb +115 -0
- data/test/cases/adapters/postgresql/quoting_test.rb +30 -0
- data/test/cases/adapters/postgresql/schema_statements_test.rb +29 -0
- data/test/cases/adapters/postgresql/serial_test.rb +199 -0
- data/test/cases/adapters/postgresql/spatial_queries_test.rb +118 -0
- data/test/cases/adapters/postgresql/spatial_setup_test.rb +18 -0
- data/test/cases/adapters/postgresql/spatial_type_test.rb +41 -0
- data/test/cases/adapters/postgresql/timestamp_test.rb +58 -0
- data/test/cases/adapters/postgresql/virtual_column_test.rb +39 -0
- data/test/cases/associations/eager_load_nested_include_test.rb +111 -0
- data/test/cases/associations/left_outer_join_association_test.rb +30 -0
- data/test/cases/associations_test.rb +108 -0
- data/test/cases/base_test.rb +31 -0
- data/test/cases/comment_test.rb +75 -0
- data/test/cases/connection_adapters/type_test.rb +28 -0
- data/test/cases/database_configurations/resolver_test.rb +24 -0
- data/test/cases/defaults_test.rb +45 -0
- data/test/cases/dirty_test.rb +24 -0
- data/test/cases/fixtures_test.rb +541 -0
- data/test/cases/helper_cockroachdb.rb +232 -0
- data/test/cases/inheritance_test.rb +42 -0
- data/test/cases/invertible_migration_test.rb +73 -0
- data/test/cases/marshal_serialization_test.rb +45 -0
- data/test/cases/migration/change_schema_test.rb +140 -0
- data/test/cases/migration/check_constraint_test.rb +125 -0
- data/test/cases/migration/columns_test.rb +50 -0
- data/test/cases/migration/create_join_table_test.rb +66 -0
- data/test/cases/migration/foreign_key_test.rb +390 -0
- data/test/cases/migration/hidden_column_test.rb +70 -0
- data/test/cases/migration/references_foreign_key_test.rb +124 -0
- data/test/cases/migration_test.rb +120 -0
- data/test/cases/persistence_test.rb +33 -0
- data/test/cases/primary_keys_test.rb +134 -0
- data/test/cases/relation/aost_test.rb +57 -0
- data/test/cases/relation/or_test.rb +26 -0
- data/test/cases/relation/table_hints_test.rb +124 -0
- data/test/cases/relation_test.rb +26 -0
- data/test/cases/relations_test.rb +29 -0
- data/test/cases/schema_dumper_test.rb +221 -0
- data/test/cases/show_create_test.rb +14 -0
- data/test/cases/strict_loading_test.rb +34 -0
- data/test/cases/tasks/cockroachdb_rake_test.rb +113 -0
- data/test/cases/test_fixtures_test.rb +57 -0
- data/test/cases/transactions_test.rb +51 -0
- data/test/cases/unsafe_raw_sql_test.rb +22 -0
- data/test/config.yml +23 -0
- data/test/excludes/ActiveRecord/AdapterTest.rb +3 -0
- data/test/excludes/ActiveRecord/AdapterTestWithoutTransaction.rb +25 -0
- data/test/excludes/ActiveRecord/ConnectionAdapters/PoolConfig/ResolverTest.rb +1 -0
- data/test/excludes/ActiveRecord/ConnectionAdapters/PostgreSQLAdapter/BindParameterTest.rb +15 -0
- data/test/excludes/ActiveRecord/ConnectionAdapters/PostgreSQLAdapter/QuotingTest.rb +22 -0
- data/test/excludes/ActiveRecord/ConnectionAdapters/PostgreSQLAdapterPreventWritesLegacyTest.rb +1 -0
- data/test/excludes/ActiveRecord/ConnectionAdapters/PostgreSQLAdapterPreventWritesTest.rb +1 -0
- data/test/excludes/ActiveRecord/ConnectionAdapters/PostgreSQLAdapterTest.rb +40 -0
- data/test/excludes/ActiveRecord/ConnectionAdapters/RegistrationIsolatedTest.rb +14 -0
- data/test/excludes/ActiveRecord/Encryption/EncryptionPerformanceTest.rb +1 -0
- data/test/excludes/ActiveRecord/Encryption/EnvelopeEncryptionPerformanceTest.rb +1 -0
- data/test/excludes/ActiveRecord/Encryption/ExtendedDeterministicQueriesPerformanceTest.rb +1 -0
- data/test/excludes/ActiveRecord/Encryption/StoragePerformanceTest.rb +2 -0
- data/test/excludes/ActiveRecord/InstrumentationTest.rb +16 -0
- data/test/excludes/ActiveRecord/InvertibleMigrationTest.rb +2 -0
- data/test/excludes/ActiveRecord/Migration/ChangeSchemaTest.rb +7 -0
- data/test/excludes/ActiveRecord/Migration/CheckConstraintTest.rb +6 -0
- data/test/excludes/ActiveRecord/Migration/ColumnsTest.rb +4 -0
- data/test/excludes/ActiveRecord/Migration/CompatibilityTest.rb +50 -0
- data/test/excludes/ActiveRecord/Migration/CompositeForeignKeyTest.rb +2 -0
- data/test/excludes/ActiveRecord/Migration/CreateJoinTableTest.rb +2 -0
- data/test/excludes/ActiveRecord/Migration/ForeignKeyInCreateTest.rb +1 -0
- data/test/excludes/ActiveRecord/Migration/ForeignKeyTest.rb +35 -0
- data/test/excludes/ActiveRecord/Migration/InvalidOptionsTest.rb +14 -0
- data/test/excludes/ActiveRecord/Migration/PGChangeSchemaTest.rb +8 -0
- data/test/excludes/ActiveRecord/Migration/ReferencesForeignKeyTest.rb +7 -0
- data/test/excludes/ActiveRecord/Migration/ReferencesIndexTest.rb +7 -0
- data/test/excludes/ActiveRecord/Migration/ReferencesStatementsTest.rb +3 -0
- data/test/excludes/ActiveRecord/MysqlDBCreateWithInvalidPermissionsTest.rb +1 -0
- data/test/excludes/ActiveRecord/OrTest.rb +1 -0
- data/test/excludes/ActiveRecord/PostgreSQLStructureDumpTest.rb +10 -0
- data/test/excludes/ActiveRecord/PostgresqlConnectionTest.rb +12 -0
- data/test/excludes/ActiveRecord/PostgresqlTransactionNestedTest.rb +14 -0
- data/test/excludes/ActiveRecord/PostgresqlTransactionTest.rb +4 -0
- data/test/excludes/ActiveRecord/RelationTest.rb +2 -0
- data/test/excludes/ActiveRecord/TooManyOrTest.rb +1 -0
- data/test/excludes/ActiveSupportSubclassWithFixturesTest.rb +1 -0
- data/test/excludes/AssociationDeprecationTest/NotifyModeTest.rb +2 -0
- data/test/excludes/AssociationDeprecationTest/RaiseBacktraceModeTest.rb +2 -0
- data/test/excludes/AssociationDeprecationTest/RaiseModeTest.rb +2 -0
- data/test/excludes/AssociationDeprecationTest/WarnBacktraceModeTest.rb +2 -0
- data/test/excludes/AssociationDeprecationTest/WarnModeTest.rb +2 -0
- data/test/excludes/AssociationDeprecationTest/fix_backtrace_cleaner.rb +10 -0
- data/test/excludes/BasicsTest.rb +1 -0
- data/test/excludes/BulkAlterTableMigrationsTest.rb +7 -0
- data/test/excludes/CalculationsTest.rb +4 -0
- data/test/excludes/CommentTest.rb +2 -0
- data/test/excludes/CoreTest.rb +1 -0
- data/test/excludes/CreateOrFindByWithinTransactions.rb +3 -0
- data/test/excludes/DefaultsUsingMultipleSchemasAndDomainTest.rb +7 -0
- data/test/excludes/DirtyTest.rb +3 -0
- data/test/excludes/EachTest.rb +8 -0
- data/test/excludes/EagerLoadPolyAssocsTest.rb +1 -0
- data/test/excludes/ExplicitlyNamedIndexMigrationTest.rb +1 -0
- data/test/excludes/FixturesResetPkSequenceTest.rb +3 -0
- data/test/excludes/FixturesTest.rb +21 -0
- data/test/excludes/FixturesWithForeignKeyViolationsTest.rb +2 -0
- data/test/excludes/ForeignTableTest.rb +10 -0
- data/test/excludes/InheritanceComputeTypeTest.rb +1 -0
- data/test/excludes/LeftOuterJoinAssociationTest.rb +2 -0
- data/test/excludes/LegacyPrimaryKeyTest/V4_2.rb +6 -0
- data/test/excludes/LegacyPrimaryKeyTest/V5_0.rb +6 -0
- data/test/excludes/MarshalSerializationTest.rb +4 -0
- data/test/excludes/MaterializedViewTest.rb +13 -0
- data/test/excludes/MigrationTest.rb +2 -0
- data/test/excludes/MultiDbMigratorTest.rb +2 -0
- data/test/excludes/NestedRelationScopingTest.rb +1 -0
- data/test/excludes/OrTest.rb +1 -0
- data/test/excludes/PersistenceTest.rb +3 -0
- data/test/excludes/PessimisticLockingTest.rb +1 -0
- data/test/excludes/PostgreSQLExplainTest.rb +7 -0
- data/test/excludes/PostgreSQLGeometricLineTest.rb +3 -0
- data/test/excludes/PostgreSQLGeometricTypesTest.rb +7 -0
- data/test/excludes/PostgreSQLPartitionsTest.rb +1 -0
- data/test/excludes/PostgreSQLReferentialIntegrityTest.rb +14 -0
- data/test/excludes/PostgresqlArrayTest.rb +21 -0
- data/test/excludes/PostgresqlBigSerialTest.rb +7 -0
- data/test/excludes/PostgresqlBitStringTest.rb +2 -0
- data/test/excludes/PostgresqlByteaTest.rb +1 -0
- data/test/excludes/PostgresqlCitextTest.rb +7 -0
- data/test/excludes/PostgresqlCollationTest.rb +5 -0
- data/test/excludes/PostgresqlCompositeTest.rb +2 -0
- data/test/excludes/PostgresqlCompositeWithCustomOIDTest.rb +2 -0
- data/test/excludes/PostgresqlDataTypeTest.rb +9 -0
- data/test/excludes/PostgresqlDefaultExpressionTest.rb +1 -0
- data/test/excludes/PostgresqlDeferredConstraintsTest.rb +3 -0
- data/test/excludes/PostgresqlDomainTest.rb +2 -0
- data/test/excludes/PostgresqlExtensionMigrationTest.rb +5 -0
- data/test/excludes/PostgresqlFullTextTest.rb +3 -0
- data/test/excludes/PostgresqlGeometricTest.rb +4 -0
- data/test/excludes/PostgresqlHstoreTest.rb +45 -0
- data/test/excludes/PostgresqlInfinityTest.rb +5 -0
- data/test/excludes/PostgresqlIntervalTest.rb +1 -0
- data/test/excludes/PostgresqlJSONBTest.rb +27 -0
- data/test/excludes/PostgresqlJSONTest.rb +27 -0
- data/test/excludes/PostgresqlLtreeTest.rb +4 -0
- data/test/excludes/PostgresqlMoneyTest.rb +12 -0
- data/test/excludes/PostgresqlNetworkTest.rb +69 -0
- data/test/excludes/PostgresqlNumberTest.rb +1 -0
- data/test/excludes/PostgresqlPointTest.rb +15 -0
- data/test/excludes/PostgresqlRangeTest.rb +3 -0
- data/test/excludes/PostgresqlRenameTableTest.rb +2 -0
- data/test/excludes/PostgresqlSerialTest.rb +7 -0
- data/test/excludes/PostgresqlTimestampFixtureTest.rb +2 -0
- data/test/excludes/PostgresqlTimestampMigrationTest.rb +3 -0
- data/test/excludes/PostgresqlTypeLookupTest.rb +2 -0
- data/test/excludes/PostgresqlUUIDGenerationTest.rb +7 -0
- data/test/excludes/PostgresqlUUIDTest.rb +1 -0
- data/test/excludes/PostgresqlVirtualColumnTest.rb +17 -0
- data/test/excludes/PostgresqlXMLTest.rb +5 -0
- data/test/excludes/PrimaryKeyIntegerNilDefaultTest.rb +1 -0
- data/test/excludes/PrimaryKeyIntegerTest.rb +3 -0
- data/test/excludes/PrimaryKeysTest.rb +2 -0
- data/test/excludes/QueryCacheExpiryTest.rb +10 -0
- data/test/excludes/RelationMergingTest.rb +15 -0
- data/test/excludes/RelationTest.rb +3 -0
- data/test/excludes/ReservedWordsMigrationTest.rb +1 -0
- data/test/excludes/SameNameDifferentDatabaseFixturesTest.rb +1 -0
- data/test/excludes/SanitizeTest.rb +13 -0
- data/test/excludes/SchemaAuthorizationTest.rb +8 -0
- data/test/excludes/SchemaCreateTableOptionsTest.rb +2 -0
- data/test/excludes/SchemaDumperDefaultsTest.rb +1 -0
- data/test/excludes/SchemaDumperTest.rb +11 -0
- data/test/excludes/SchemaForeignKeyTest.rb +1 -0
- data/test/excludes/SchemaIndexNullsOrderTest.rb +2 -0
- data/test/excludes/SchemaIndexOpclassTest.rb +3 -0
- data/test/excludes/SchemaTest.rb +49 -0
- data/test/excludes/SequenceNameDetectionTestCases/CollidedSequenceNameTest.rb +1 -0
- data/test/excludes/SequenceNameDetectionTestCases/LongerSequenceNameDetectionTest.rb +1 -0
- data/test/excludes/StrictLoadingFixturesTest.rb +1 -0
- data/test/excludes/TestFixturesTest.rb +1 -0
- data/test/excludes/TransactionInstrumentationTest.rb +1 -0
- data/test/excludes/TransactionIsolationTest.rb +1 -0
- data/test/excludes/TypeTest.rb +1 -0
- data/test/excludes/UniquenessValidationTest.rb +2 -0
- data/test/excludes/UnloggedTablesTest.rb +3 -0
- data/test/excludes/UnsafeRawSqlTest.rb +2 -0
- data/test/excludes/UpdateableViewTest.rb +5 -0
- data/test/excludes/WithAnnotationsTest.rb +6 -0
- data/test/models/building.rb +4 -0
- data/test/models/spatial_model.rb +5 -0
- data/test/schema/cockroachdb_specific_schema.rb +218 -0
- data/test/support/copy_cat.rb +83 -0
- data/test/support/exclude_from_transactional_tests.rb +33 -0
- data/test/support/paths_cockroachdb.rb +46 -0
- data/test/support/rake_helpers.rb +37 -0
- data/test/support/sql_logger.rb +55 -0
- data/test/support/template_creator.rb +114 -0
- metadata +422 -30
- data/.editorconfig +0 -7
- data/.github/issue_template.md +0 -46
- data/.github/reproduction_scripts/migrations.rb +0 -60
- data/.github/reproduction_scripts/models_and_database.rb +0 -49
- data/.github/workflows/ci.yml +0 -96
- data/.github/workflows/docker.yml +0 -57
- data/.gitignore +0 -18
- data/.gitmodules +0 -0
- data/activerecord-cockroachdb-adapter.gemspec +0 -38
- data/bin/console +0 -36
- data/bin/console_schemas/default.rb +0 -11
- data/bin/console_schemas/schemas.rb +0 -23
- data/bin/setup +0 -8
- data/bin/start-cockroachdb +0 -33
- data/build/Dockerfile +0 -14
- data/build/config.teamcity.yml +0 -31
- data/build/local-test.sh +0 -32
- data/build/teamcity-test.sh +0 -76
- data/docker.sh +0 -35
- data/lib/active_record/connection_adapters/cockroachdb/spatial_column_info.rb +0 -60
- data/setup.sql +0 -18
|
@@ -20,6 +20,91 @@ module ActiveRecord
|
|
|
20
20
|
module SchemaStatements
|
|
21
21
|
include ActiveRecord::ConnectionAdapters::PostgreSQL::SchemaStatements
|
|
22
22
|
|
|
23
|
+
# OVERRIDE(v8.1.1):
|
|
24
|
+
# - prepend Utils with PostgreSQL::
|
|
25
|
+
# - handle hidden attributes
|
|
26
|
+
# Returns an array of indexes for the given table.
|
|
27
|
+
def indexes(table_name) # :nodoc:
|
|
28
|
+
scope = quoted_scope(table_name)
|
|
29
|
+
|
|
30
|
+
result = query(<<~SQL, "SCHEMA")
|
|
31
|
+
SELECT distinct i.relname, d.indisunique, d.indkey, pg_get_indexdef(d.indexrelid),
|
|
32
|
+
pg_catalog.obj_description(i.oid, 'pg_class') AS comment, d.indisvalid,
|
|
33
|
+
ARRAY(
|
|
34
|
+
SELECT pg_get_indexdef(d.indexrelid, k + 1, true)
|
|
35
|
+
FROM generate_subscripts(d.indkey, 1) AS k
|
|
36
|
+
ORDER BY k
|
|
37
|
+
) AS columns,
|
|
38
|
+
ARRAY(
|
|
39
|
+
SELECT a.attname
|
|
40
|
+
FROM pg_attribute a
|
|
41
|
+
WHERE a.attrelid = d.indexrelid AND a.attishidden
|
|
42
|
+
) AS hidden_columns
|
|
43
|
+
FROM pg_class t
|
|
44
|
+
INNER JOIN pg_index d ON t.oid = d.indrelid
|
|
45
|
+
INNER JOIN pg_class i ON d.indexrelid = i.oid
|
|
46
|
+
LEFT JOIN pg_namespace n ON n.oid = t.relnamespace
|
|
47
|
+
WHERE i.relkind IN ('i', 'I')
|
|
48
|
+
AND d.indisprimary = 'f'
|
|
49
|
+
AND t.relname = #{scope[:name]}
|
|
50
|
+
AND n.nspname = #{scope[:schema]}
|
|
51
|
+
ORDER BY i.relname
|
|
52
|
+
SQL
|
|
53
|
+
|
|
54
|
+
unquote = -> (column) {
|
|
55
|
+
PostgreSQL::Utils.unquote_identifier(column.strip.gsub('""', '"'))
|
|
56
|
+
}
|
|
57
|
+
result.map do |row|
|
|
58
|
+
index_name = row[0]
|
|
59
|
+
unique = row[1]
|
|
60
|
+
indkey = row[2].split(" ").map(&:to_i)
|
|
61
|
+
inddef = row[3]
|
|
62
|
+
comment = row[4]
|
|
63
|
+
valid = row[5]
|
|
64
|
+
columns = decode_string_array(row[6]).map(&unquote)
|
|
65
|
+
hidden_columns = decode_string_array(row[7]).map(&unquote)
|
|
66
|
+
|
|
67
|
+
using, expressions, include, nulls_not_distinct, where = inddef.scan(/ USING (\w+?) \((.+?)\)(?: INCLUDE \((.+?)\))?( NULLS NOT DISTINCT)?(?: WHERE (.+))?\z/m).flatten
|
|
68
|
+
|
|
69
|
+
orders = {}
|
|
70
|
+
opclasses = {}
|
|
71
|
+
include_columns = include ? include.split(",").map(&unquote) : []
|
|
72
|
+
|
|
73
|
+
if indkey.include?(0)
|
|
74
|
+
columns = expressions
|
|
75
|
+
else
|
|
76
|
+
# prevent INCLUDE and hidden columns from being matched
|
|
77
|
+
columns.reject! { |c| include_columns.include?(c) || hidden_columns.include?(c) }
|
|
78
|
+
|
|
79
|
+
# add info on sort order (only desc order is explicitly specified, asc is the default)
|
|
80
|
+
# and non-default opclasses
|
|
81
|
+
expressions.scan(/(?<column>\w+)"?\s?(?<opclass>\w+_ops(_\w+)?)?\s?(?<desc>DESC)?\s?(?<nulls>NULLS (?:FIRST|LAST))?/).each do |column, opclass, desc, nulls|
|
|
82
|
+
opclasses[column] = opclass.to_sym if opclass
|
|
83
|
+
if nulls
|
|
84
|
+
orders[column] = [desc, nulls].compact.join(" ")
|
|
85
|
+
else
|
|
86
|
+
orders[column] = :desc if desc
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
IndexDefinition.new(
|
|
92
|
+
table_name,
|
|
93
|
+
index_name,
|
|
94
|
+
unique,
|
|
95
|
+
columns,
|
|
96
|
+
orders: orders,
|
|
97
|
+
opclasses: opclasses,
|
|
98
|
+
where: where,
|
|
99
|
+
using: using.to_sym,
|
|
100
|
+
include: include_columns.presence,
|
|
101
|
+
nulls_not_distinct: nulls_not_distinct.present?,
|
|
102
|
+
comment: comment.presence,
|
|
103
|
+
valid: valid
|
|
104
|
+
)
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
23
108
|
# OVERRIDE: We do not want to see the crdb_internal schema in the names.
|
|
24
109
|
#
|
|
25
110
|
# Returns an array of schema names.
|
|
@@ -27,16 +112,6 @@ module ActiveRecord
|
|
|
27
112
|
super - ["crdb_internal"]
|
|
28
113
|
end
|
|
29
114
|
|
|
30
|
-
def add_index(table_name, column_name, **options)
|
|
31
|
-
super
|
|
32
|
-
rescue ActiveRecord::StatementInvalid => error
|
|
33
|
-
if debugging? && error.cause.class == PG::FeatureNotSupported
|
|
34
|
-
warn "#{error}\n\nThis error will be ignored and the index will not be created.\n\n"
|
|
35
|
-
else
|
|
36
|
-
raise error
|
|
37
|
-
end
|
|
38
|
-
end
|
|
39
|
-
|
|
40
115
|
# ActiveRecord allows for tables to exist without primary keys.
|
|
41
116
|
# Databases like PostgreSQL support this behavior, but CockroachDB does
|
|
42
117
|
# not. If a table is created without a primary key, CockroachDB will add
|
|
@@ -55,34 +130,18 @@ module ActiveRecord
|
|
|
55
130
|
end
|
|
56
131
|
end
|
|
57
132
|
|
|
133
|
+
# OVERRIDE(v8.1.1): handle hidden attributes
|
|
58
134
|
def primary_keys(table_name)
|
|
59
|
-
return super unless database_version >= 24_02_02
|
|
60
|
-
|
|
61
135
|
query_values(<<~SQL, "SCHEMA")
|
|
62
136
|
SELECT a.attname
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
AND a.attnum = i.indkey[i.idx]
|
|
72
|
-
AND NOT a.attishidden
|
|
73
|
-
ORDER BY i.idx
|
|
74
|
-
SQL
|
|
75
|
-
end
|
|
76
|
-
|
|
77
|
-
def column_names_from_column_numbers(table_oid, column_numbers)
|
|
78
|
-
return super unless database_version >= 24_02_02
|
|
79
|
-
|
|
80
|
-
Hash[query(<<~SQL, "SCHEMA")].values_at(*column_numbers).compact
|
|
81
|
-
SELECT a.attnum, a.attname
|
|
82
|
-
FROM pg_attribute a
|
|
83
|
-
WHERE a.attrelid = #{table_oid}
|
|
84
|
-
AND a.attnum IN (#{column_numbers.join(", ")})
|
|
85
|
-
AND NOT a.attishidden
|
|
137
|
+
FROM pg_index i
|
|
138
|
+
JOIN pg_attribute a
|
|
139
|
+
ON a.attrelid = i.indrelid
|
|
140
|
+
AND a.attnum = ANY(i.indkey)
|
|
141
|
+
AND NOT a.attishidden
|
|
142
|
+
WHERE i.indrelid = #{quote(quote_table_name(table_name))}::regclass
|
|
143
|
+
AND i.indisprimary
|
|
144
|
+
ORDER BY array_position(i.indkey, a.attnum)
|
|
86
145
|
SQL
|
|
87
146
|
end
|
|
88
147
|
|
|
@@ -94,7 +153,7 @@ module ActiveRecord
|
|
|
94
153
|
options
|
|
95
154
|
end
|
|
96
155
|
|
|
97
|
-
# OVERRIDE: Added `unique_rowid` to the last line of the second query.
|
|
156
|
+
# OVERRIDE(v8.1.1): Added `unique_rowid` to the last line of the second query.
|
|
98
157
|
# This is a CockroachDB-specific function used for primary keys.
|
|
99
158
|
# Also make sure we don't consider `NOT VISIBLE` columns.
|
|
100
159
|
#
|
|
@@ -108,11 +167,7 @@ module ActiveRecord
|
|
|
108
167
|
pg_attribute attr,
|
|
109
168
|
pg_depend dep,
|
|
110
169
|
pg_constraint cons,
|
|
111
|
-
pg_namespace nsp
|
|
112
|
-
-- TODO: use the pg_catalog.pg_attribute(attishidden) column when
|
|
113
|
-
-- it is added instead of joining on crdb_internal.
|
|
114
|
-
-- See https://github.com/cockroachdb/cockroach/pull/126397
|
|
115
|
-
crdb_internal.table_columns tc
|
|
170
|
+
pg_namespace nsp
|
|
116
171
|
WHERE seq.oid = dep.objid
|
|
117
172
|
AND seq.relkind = 'S'
|
|
118
173
|
AND attr.attrelid = dep.refobjid
|
|
@@ -120,12 +175,10 @@ module ActiveRecord
|
|
|
120
175
|
AND attr.attrelid = cons.conrelid
|
|
121
176
|
AND attr.attnum = cons.conkey[1]
|
|
122
177
|
AND seq.relnamespace = nsp.oid
|
|
123
|
-
AND attr.attrelid = tc.descriptor_id
|
|
124
|
-
AND attr.attname = tc.column_name
|
|
125
|
-
AND tc.hidden = false
|
|
126
178
|
AND cons.contype = 'p'
|
|
127
179
|
AND dep.classid = 'pg_class'::regclass
|
|
128
180
|
AND dep.refobjid = #{quote(quote_table_name(table))}::regclass
|
|
181
|
+
AND not attr.attishidden
|
|
129
182
|
SQL
|
|
130
183
|
|
|
131
184
|
if result.nil? || result.empty?
|
|
@@ -143,12 +196,8 @@ module ActiveRecord
|
|
|
143
196
|
JOIN pg_attrdef def ON (adrelid = attrelid AND adnum = attnum)
|
|
144
197
|
JOIN pg_constraint cons ON (conrelid = adrelid AND adnum = conkey[1])
|
|
145
198
|
JOIN pg_namespace nsp ON (t.relnamespace = nsp.oid)
|
|
146
|
-
-- TODO: use the pg_catalog.pg_attribute(attishidden) column when
|
|
147
|
-
-- it is added instead of joining on crdb_internal.
|
|
148
|
-
-- See https://github.com/cockroachdb/cockroach/pull/126397
|
|
149
|
-
JOIN crdb_internal.table_columns tc ON (attr.attrelid = tc.descriptor_id AND attr.attname = tc.column_name)
|
|
150
199
|
WHERE t.oid = #{quote(quote_table_name(table))}::regclass
|
|
151
|
-
AND
|
|
200
|
+
AND NOT attr.attishidden
|
|
152
201
|
AND cons.contype = 'p'
|
|
153
202
|
AND pg_get_expr(def.adbin, def.adrelid) ~* 'nextval|uuid_generate|gen_random_uuid|unique_rowid'
|
|
154
203
|
SQL
|
|
@@ -164,53 +213,66 @@ module ActiveRecord
|
|
|
164
213
|
nil
|
|
165
214
|
end
|
|
166
215
|
|
|
167
|
-
#
|
|
168
|
-
#
|
|
169
|
-
#
|
|
170
|
-
#
|
|
171
|
-
#
|
|
172
|
-
#
|
|
216
|
+
# OVERRIDE(v8.1.1):
|
|
217
|
+
# - Replaces t2.oid::regclass::text with t2.relname
|
|
218
|
+
# since this is more efficient in CockroachDB.
|
|
219
|
+
# - prepend schema name to relname (see `AS to_table`)
|
|
220
|
+
# - handle hidden attributes.
|
|
221
|
+
#
|
|
222
|
+
# NOTE: If you edit this method, you'll need to edit
|
|
223
|
+
# the `#all_foreign_keys` method as well.
|
|
173
224
|
def foreign_keys(table_name)
|
|
174
225
|
scope = quoted_scope(table_name)
|
|
175
|
-
fk_info = internal_exec_query(<<~SQL, "SCHEMA")
|
|
226
|
+
fk_info = internal_exec_query(<<~SQL, "SCHEMA", allow_retry: true, materialize_transactions: false)
|
|
176
227
|
SELECT CASE
|
|
177
228
|
WHEN n2.nspname = current_schema()
|
|
178
229
|
THEN ''
|
|
179
230
|
ELSE n2.nspname || '.'
|
|
180
231
|
END || t2.relname AS to_table,
|
|
181
|
-
|
|
182
|
-
|
|
232
|
+
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, c.conrelid, c.confrelid,
|
|
233
|
+
(
|
|
234
|
+
SELECT array_agg(a.attname ORDER BY idx)
|
|
235
|
+
FROM (
|
|
236
|
+
SELECT idx, c.conkey[idx] AS conkey_elem
|
|
237
|
+
FROM generate_subscripts(c.conkey, 1) AS idx
|
|
238
|
+
) indexed_conkeys
|
|
239
|
+
JOIN pg_attribute a ON a.attrelid = t1.oid
|
|
240
|
+
AND a.attnum = indexed_conkeys.conkey_elem
|
|
241
|
+
AND NOT a.attishidden
|
|
242
|
+
) AS conkey_names,
|
|
243
|
+
(
|
|
244
|
+
SELECT array_agg(a.attname ORDER BY idx)
|
|
245
|
+
FROM (
|
|
246
|
+
SELECT idx, c.confkey[idx] AS confkey_elem
|
|
247
|
+
FROM generate_subscripts(c.confkey, 1) AS idx
|
|
248
|
+
) indexed_confkeys
|
|
249
|
+
JOIN pg_attribute a ON a.attrelid = t2.oid
|
|
250
|
+
AND a.attnum = indexed_confkeys.confkey_elem
|
|
251
|
+
AND NOT a.attishidden
|
|
252
|
+
) AS confkey_names
|
|
183
253
|
FROM pg_constraint c
|
|
184
254
|
JOIN pg_class t1 ON c.conrelid = t1.oid
|
|
185
255
|
JOIN pg_class t2 ON c.confrelid = t2.oid
|
|
186
|
-
JOIN
|
|
187
|
-
JOIN pg_attribute a2 ON a2.attnum = c.confkey[1] AND a2.attrelid = t2.oid
|
|
188
|
-
JOIN pg_namespace t3 ON c.connamespace = t3.oid
|
|
256
|
+
JOIN pg_namespace n1 ON t1.relnamespace = n1.oid
|
|
189
257
|
JOIN pg_namespace n2 ON t2.relnamespace = n2.oid
|
|
190
258
|
WHERE c.contype = 'f'
|
|
191
259
|
AND t1.relname = #{scope[:name]}
|
|
192
|
-
AND
|
|
260
|
+
AND n1.nspname = #{scope[:schema]}
|
|
193
261
|
ORDER BY c.conname
|
|
194
262
|
SQL
|
|
195
263
|
|
|
196
264
|
fk_info.map do |row|
|
|
197
265
|
to_table = PostgreSQL::Utils.unquote_identifier(row["to_table"])
|
|
198
|
-
conkey = row["conkey"].scan(/\d+/).map(&:to_i)
|
|
199
|
-
confkey = row["confkey"].scan(/\d+/).map(&:to_i)
|
|
200
266
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
primary_key = column_names_from_column_numbers(row["confrelid"], confkey)
|
|
204
|
-
else
|
|
205
|
-
column = PostgreSQL::Utils.unquote_identifier(row["column"])
|
|
206
|
-
primary_key = row["primary_key"]
|
|
207
|
-
end
|
|
267
|
+
column = decode_string_array(row["conkey_names"])
|
|
268
|
+
primary_key = decode_string_array(row["confkey_names"])
|
|
208
269
|
|
|
209
270
|
options = {
|
|
210
|
-
column: column,
|
|
271
|
+
column: column.size == 1 ? column.first : column,
|
|
211
272
|
name: row["name"],
|
|
212
|
-
primary_key: primary_key
|
|
273
|
+
primary_key: primary_key.size == 1 ? primary_key.first : primary_key
|
|
213
274
|
}
|
|
275
|
+
|
|
214
276
|
options[:on_delete] = extract_foreign_key_action(row["on_delete"])
|
|
215
277
|
options[:on_update] = extract_foreign_key_action(row["on_update"])
|
|
216
278
|
options[:deferrable] = extract_constraint_deferrable(row["deferrable"], row["deferred"])
|
|
@@ -228,8 +290,51 @@ module ActiveRecord
|
|
|
228
290
|
nil
|
|
229
291
|
end
|
|
230
292
|
|
|
231
|
-
#
|
|
232
|
-
#
|
|
293
|
+
# OVERRIDE(v8.1.1): handle hidden attributes
|
|
294
|
+
#
|
|
295
|
+
# Returns an array of unique constraints for the given table.
|
|
296
|
+
# The unique constraints are represented as UniqueConstraintDefinition objects.
|
|
297
|
+
def unique_constraints(table_name)
|
|
298
|
+
scope = quoted_scope(table_name)
|
|
299
|
+
|
|
300
|
+
unique_info = internal_exec_query(<<~SQL, "SCHEMA", allow_retry: true, materialize_transactions: false)
|
|
301
|
+
SELECT c.conname, c.conrelid, c.condeferrable, c.condeferred, pg_get_constraintdef(c.oid) AS constraintdef,
|
|
302
|
+
(
|
|
303
|
+
SELECT array_agg(a.attname ORDER BY idx)
|
|
304
|
+
FROM (
|
|
305
|
+
SELECT idx, c.conkey[idx] AS conkey_elem
|
|
306
|
+
FROM generate_subscripts(c.conkey, 1) AS idx
|
|
307
|
+
) indexed_conkeys
|
|
308
|
+
JOIN pg_attribute a ON a.attrelid = t.oid
|
|
309
|
+
AND a.attnum = indexed_conkeys.conkey_elem
|
|
310
|
+
AND NOT a.attishidden
|
|
311
|
+
) AS conkey_names
|
|
312
|
+
FROM pg_constraint c
|
|
313
|
+
JOIN pg_class t ON c.conrelid = t.oid
|
|
314
|
+
JOIN pg_namespace n ON n.oid = c.connamespace
|
|
315
|
+
WHERE c.contype = 'u'
|
|
316
|
+
AND t.relname = #{scope[:name]}
|
|
317
|
+
AND n.nspname = #{scope[:schema]}
|
|
318
|
+
SQL
|
|
319
|
+
|
|
320
|
+
unique_info.map do |row|
|
|
321
|
+
columns = decode_string_array(row["conkey_names"])
|
|
322
|
+
|
|
323
|
+
nulls_not_distinct = row["constraintdef"].start_with?("UNIQUE NULLS NOT DISTINCT")
|
|
324
|
+
deferrable = extract_constraint_deferrable(row["condeferrable"], row["condeferred"])
|
|
325
|
+
|
|
326
|
+
options = {
|
|
327
|
+
name: row["conname"],
|
|
328
|
+
nulls_not_distinct: nulls_not_distinct,
|
|
329
|
+
deferrable: deferrable
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
UniqueConstraintDefinition.new(table_name, columns, options)
|
|
333
|
+
end
|
|
334
|
+
end
|
|
335
|
+
|
|
336
|
+
# OVERRIDE(v8.1.1):
|
|
337
|
+
# - Add hidden information
|
|
233
338
|
def new_column_from_field(table_name, field, _definition)
|
|
234
339
|
column_name, type, default, notnull, oid, fmod, collation, comment, identity, attgenerated, hidden = field
|
|
235
340
|
type_metadata = fetch_type_metadata(column_name, type, oid.to_i, fmod.to_i)
|
|
@@ -245,11 +350,9 @@ module ActiveRecord
|
|
|
245
350
|
serial = sequence_name_from_parts(table_name, column_name, match[:suffix]) == match[:sequence_name]
|
|
246
351
|
end
|
|
247
352
|
|
|
248
|
-
# {:dimension=>2, :has_m=>false, :has_z=>false, :name=>"latlon", :srid=>0, :type=>"GEOMETRY"}
|
|
249
|
-
spatial = spatial_column_info(table_name).get(column_name, type_metadata.sql_type)
|
|
250
|
-
|
|
251
353
|
CockroachDB::Column.new(
|
|
252
354
|
column_name,
|
|
355
|
+
get_oid_type(oid.to_i, fmod.to_i, column_name, type),
|
|
253
356
|
default_value,
|
|
254
357
|
type_metadata,
|
|
255
358
|
!notnull,
|
|
@@ -258,7 +361,6 @@ module ActiveRecord
|
|
|
258
361
|
comment: comment.presence,
|
|
259
362
|
serial: serial,
|
|
260
363
|
identity: identity.presence,
|
|
261
|
-
spatial: spatial,
|
|
262
364
|
generated: attgenerated,
|
|
263
365
|
hidden: hidden
|
|
264
366
|
)
|
|
@@ -295,34 +397,11 @@ module ActiveRecord
|
|
|
295
397
|
sql
|
|
296
398
|
end
|
|
297
399
|
|
|
298
|
-
# override
|
|
299
|
-
def native_database_types
|
|
300
|
-
# Add spatial types
|
|
301
|
-
super.merge(
|
|
302
|
-
geography: { name: "geography" },
|
|
303
|
-
geometry: { name: "geometry" },
|
|
304
|
-
geometry_collection: { name: "geometry_collection" },
|
|
305
|
-
line_string: { name: "line_string" },
|
|
306
|
-
multi_line_string: { name: "multi_line_string" },
|
|
307
|
-
multi_point: { name: "multi_point" },
|
|
308
|
-
multi_polygon: { name: "multi_polygon" },
|
|
309
|
-
spatial: { name: "geometry" },
|
|
310
|
-
st_point: { name: "st_point" },
|
|
311
|
-
st_polygon: { name: "st_polygon" }
|
|
312
|
-
)
|
|
313
|
-
end
|
|
314
|
-
|
|
315
400
|
# override
|
|
316
401
|
def create_table_definition(*args, **kwargs)
|
|
317
402
|
CockroachDB::TableDefinition.new(self, *args, **kwargs)
|
|
318
403
|
end
|
|
319
404
|
|
|
320
|
-
# memoize hash of column infos for tables
|
|
321
|
-
def spatial_column_info(table_name)
|
|
322
|
-
@spatial_column_info ||= {}
|
|
323
|
-
@spatial_column_info[table_name.to_sym] ||= SpatialColumnInfo.new(self, table_name.to_s)
|
|
324
|
-
end
|
|
325
|
-
|
|
326
405
|
def create_schema_dumper(options)
|
|
327
406
|
CockroachDB::SchemaDumper.create(self, options)
|
|
328
407
|
end
|
|
@@ -30,7 +30,6 @@ require "active_record/connection_adapters/cockroachdb/table_definition"
|
|
|
30
30
|
require "active_record/connection_adapters/cockroachdb/quoting"
|
|
31
31
|
require "active_record/connection_adapters/cockroachdb/type"
|
|
32
32
|
require "active_record/connection_adapters/cockroachdb/column"
|
|
33
|
-
require "active_record/connection_adapters/cockroachdb/spatial_column_info"
|
|
34
33
|
require "active_record/connection_adapters/cockroachdb/setup"
|
|
35
34
|
require "active_record/connection_adapters/cockroachdb/oid/spatial"
|
|
36
35
|
require "active_record/connection_adapters/cockroachdb/oid/interval"
|
|
@@ -111,6 +110,23 @@ module ActiveRecord
|
|
|
111
110
|
SPATIAL_COLUMN_OPTIONS[key]
|
|
112
111
|
end
|
|
113
112
|
|
|
113
|
+
def self.native_database_types
|
|
114
|
+
return @native_database_types if defined?(@native_database_types)
|
|
115
|
+
# Add spatial types
|
|
116
|
+
@native_database_types = super.merge(
|
|
117
|
+
geography: { name: "geography" },
|
|
118
|
+
geometry: { name: "geometry" },
|
|
119
|
+
geometry_collection: { name: "geometry_collection" },
|
|
120
|
+
line_string: { name: "line_string" },
|
|
121
|
+
multi_line_string: { name: "multi_line_string" },
|
|
122
|
+
multi_point: { name: "multi_point" },
|
|
123
|
+
multi_polygon: { name: "multi_polygon" },
|
|
124
|
+
spatial: { name: "geometry" },
|
|
125
|
+
st_point: { name: "st_point" },
|
|
126
|
+
st_polygon: { name: "st_polygon" }
|
|
127
|
+
)
|
|
128
|
+
end
|
|
129
|
+
|
|
114
130
|
def postgis_lib_version
|
|
115
131
|
@postgis_lib_version ||= select_value("SELECT PostGIS_Lib_Version()")
|
|
116
132
|
end
|
|
@@ -128,10 +144,6 @@ module ActiveRecord
|
|
|
128
144
|
}
|
|
129
145
|
end
|
|
130
146
|
|
|
131
|
-
def debugging?
|
|
132
|
-
!!ENV["DEBUG_COCKROACHDB_ADAPTER"]
|
|
133
|
-
end
|
|
134
|
-
|
|
135
147
|
def max_transaction_retries
|
|
136
148
|
@max_transaction_retries ||= @config.fetch(:max_transaction_retries, 3)
|
|
137
149
|
end
|
|
@@ -190,6 +202,12 @@ module ActiveRecord
|
|
|
190
202
|
false
|
|
191
203
|
end
|
|
192
204
|
|
|
205
|
+
def supports_restart_db_transaction?
|
|
206
|
+
# In PostgreSQL, this would call 'ROLLBACK AND CHAIN'
|
|
207
|
+
# which is not available with CRDB.
|
|
208
|
+
false
|
|
209
|
+
end
|
|
210
|
+
|
|
193
211
|
# OVERRIDE: UNIQUE CONSTRAINTS will create indexes anyway, so we only consider
|
|
194
212
|
# then as indexes.
|
|
195
213
|
# See https://github.com/cockroachdb/activerecord-cockroachdb-adapter/issues/347.
|
|
@@ -233,6 +251,10 @@ module ActiveRecord
|
|
|
233
251
|
false
|
|
234
252
|
end
|
|
235
253
|
|
|
254
|
+
def supports_close_prepared?
|
|
255
|
+
true
|
|
256
|
+
end
|
|
257
|
+
|
|
236
258
|
def check_version # :nodoc:
|
|
237
259
|
# https://www.cockroachlabs.com/docs/releases/release-support-policy
|
|
238
260
|
if database_version < 23_01_12 # < 23.1.12
|
|
@@ -243,6 +265,26 @@ module ActiveRecord
|
|
|
243
265
|
def configure_connection(...)
|
|
244
266
|
super
|
|
245
267
|
|
|
268
|
+
# Starting in CockroachDB v26.x, tables are created with the
|
|
269
|
+
# `schema_locked` storage parameter enabled by default (controlled by the
|
|
270
|
+
# `sql.create_table_with_schema_locked.enabled` cluster setting). While it
|
|
271
|
+
# improves changefeed performance, it blocks the transactional DDL that
|
|
272
|
+
# Active Record migrations and the test suite rely on: CockroachDB can
|
|
273
|
+
# only auto-unlock a locked table for single-statement implicit
|
|
274
|
+
# transactions, not for DDL run inside a transaction. Opt out at the
|
|
275
|
+
# session level so Active Record keeps working out of the box; users who
|
|
276
|
+
# want the changefeed benefit can still lock individual tables or set
|
|
277
|
+
# `create_table_with_schema_locked: true` in their connection variables.
|
|
278
|
+
#
|
|
279
|
+
# The session variable was introduced in CockroachDB v25.3, and we let an
|
|
280
|
+
# explicit `:variables` entry take precedence over this default.
|
|
281
|
+
#
|
|
282
|
+
# See https://www.cockroachlabs.com/docs/stable/schema-locked
|
|
283
|
+
variables = @config.fetch(:variables, {}).stringify_keys
|
|
284
|
+
if database_version >= 25_03_00 && !variables.key?("create_table_with_schema_locked")
|
|
285
|
+
internal_execute("SET create_table_with_schema_locked = false", "SCHEMA")
|
|
286
|
+
end
|
|
287
|
+
|
|
246
288
|
# This rescue flow appears in new_client, but it is needed here as well
|
|
247
289
|
# since Cockroach will sometimes not raise until a query is made.
|
|
248
290
|
#
|
|
@@ -299,7 +341,7 @@ module ActiveRecord
|
|
|
299
341
|
st_polygon
|
|
300
342
|
).each do |geo_type|
|
|
301
343
|
m.register_type(geo_type) do |oid, _, sql_type|
|
|
302
|
-
CockroachDB::OID::Spatial.new(oid, sql_type)
|
|
344
|
+
CockroachDB::OID::Spatial.new(oid, sql_type).freeze
|
|
303
345
|
end
|
|
304
346
|
end
|
|
305
347
|
|
|
@@ -378,18 +420,10 @@ module ActiveRecord
|
|
|
378
420
|
nil
|
|
379
421
|
end
|
|
380
422
|
|
|
381
|
-
#
|
|
382
|
-
#
|
|
383
|
-
#
|
|
384
|
-
#
|
|
385
|
-
# as the field parameter. This data is then used to format the column
|
|
386
|
-
# objects for the model and sent to the OID for data casting.
|
|
387
|
-
#
|
|
388
|
-
# Sometimes there are differences between how data is formatted
|
|
389
|
-
# in Postgres and CockroachDB, so additional queries for certain types
|
|
390
|
-
# may be necessary to properly form the column definition.
|
|
391
|
-
#
|
|
392
|
-
# @see: https://github.com/rails/rails/blob/8695b028261bdd244e254993255c6641bdbc17a5/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb#L829
|
|
423
|
+
# OVERRIDE(v8.1.1):
|
|
424
|
+
# - comment is retrieved differently than PG for performance
|
|
425
|
+
# - gather detailed information about spatial columns. See
|
|
426
|
+
# `#crdb_column_definitions`
|
|
393
427
|
def column_definitions(table_name)
|
|
394
428
|
fields = query(<<~SQL, "SCHEMA")
|
|
395
429
|
SELECT a.attname, format_type(a.atttypid, a.atttypmod),
|
|
@@ -397,7 +431,7 @@ module ActiveRecord
|
|
|
397
431
|
c.collname, NULL AS comment,
|
|
398
432
|
attidentity,
|
|
399
433
|
attgenerated,
|
|
400
|
-
|
|
434
|
+
a.attishidden
|
|
401
435
|
FROM pg_attribute a
|
|
402
436
|
LEFT JOIN pg_attrdef d ON a.attrelid = d.adrelid AND a.attnum = d.adnum
|
|
403
437
|
LEFT JOIN pg_type t ON a.atttypid = t.oid
|
|
@@ -428,7 +462,6 @@ module ActiveRecord
|
|
|
428
462
|
dtype = field[f_type]
|
|
429
463
|
field[f_type] = crdb_fields[field[f_attname]][2].downcase if re.match(dtype)
|
|
430
464
|
field[f_comment] = crdb_fields[field[f_attname]][1]
|
|
431
|
-
field[f_is_hidden] = true if crdb_fields[field[f_attname]][3]
|
|
432
465
|
field
|
|
433
466
|
end
|
|
434
467
|
fields.delete_if do |field|
|
|
@@ -444,13 +477,24 @@ module ActiveRecord
|
|
|
444
477
|
# column_definitions. This will include limit,
|
|
445
478
|
# precision, and scale information in the type.
|
|
446
479
|
# Ex. geometry -> geometry(point, 4326)
|
|
480
|
+
#
|
|
481
|
+
# The performance difference to fetch comments is very
|
|
482
|
+
# significant. On a M1 Mac, CockroachDB v25.2.2 and
|
|
483
|
+
# data from the test suite, it is 20x times faster.
|
|
484
|
+
#
|
|
485
|
+
# SELECT col_description(a.attrelid, a.attnum)
|
|
486
|
+
# FROM pg_attribute a; -- 1.052s
|
|
487
|
+
#
|
|
488
|
+
# SELECT c.column_comment
|
|
489
|
+
# FROM information_schema.columns c; -- 50ms
|
|
490
|
+
#
|
|
447
491
|
def crdb_column_definitions(table_name)
|
|
448
492
|
table_name = PostgreSQL::Utils.extract_schema_qualified_name(table_name)
|
|
449
493
|
table = table_name.identifier
|
|
450
494
|
with_schema = " AND c.table_schema = #{quote(table_name.schema)}" if table_name.schema
|
|
451
495
|
fields = \
|
|
452
496
|
query(<<~SQL, "SCHEMA")
|
|
453
|
-
SELECT c.column_name, c.column_comment, c.crdb_sql_type
|
|
497
|
+
SELECT c.column_name, c.column_comment, c.crdb_sql_type
|
|
454
498
|
FROM information_schema.columns c
|
|
455
499
|
WHERE c.table_name = #{quote(table)}#{with_schema}
|
|
456
500
|
SQL
|
|
@@ -461,22 +505,22 @@ module ActiveRecord
|
|
|
461
505
|
end
|
|
462
506
|
|
|
463
507
|
# override
|
|
464
|
-
#
|
|
465
|
-
#
|
|
466
|
-
# be an ActiveRecord::PreparedStatementCacheExpired
|
|
467
|
-
# error.
|
|
468
|
-
#
|
|
469
|
-
# ActiveRecord handles this by checking that the sql state matches the
|
|
470
|
-
# FEATURE_NOT_SUPPORTED code and that the source function
|
|
471
|
-
# is "RevalidateCachedQuery" since that is the only function
|
|
472
|
-
# in postgres that will create this error.
|
|
508
|
+
# Classifies a FEATURE_NOT_SUPPORTED error from the PG gem as an
|
|
509
|
+
# ActiveRecord::PreparedStatementCacheExpired.
|
|
473
510
|
#
|
|
474
|
-
#
|
|
475
|
-
#
|
|
476
|
-
#
|
|
511
|
+
# Upstream matches PG_DIAG_SOURCE_FUNCTION == "RevalidateCachedQuery",
|
|
512
|
+
# the one PostgreSQL C function that raises this error. CockroachDB's
|
|
513
|
+
# raising function is an internal detail that has already moved
|
|
514
|
+
# (runExecBuilder before cockroachdb/cockroach#164406, execBind after),
|
|
515
|
+
# so match the message instead. The message is not formally guaranteed,
|
|
516
|
+
# but it has been unchanged in PostgreSQL since 8.3 and drivers such as
|
|
517
|
+
# pgx assert the exact string. Upstream avoids message matching because
|
|
518
|
+
# PostgreSQL localizes messages via lc_messages (rails/rails@d507ae2a74);
|
|
519
|
+
# CockroachDB does not localize, so that concern does not apply.
|
|
520
|
+
CACHED_PLAN_HEURISTIC = "cached plan must not change result type"
|
|
477
521
|
def is_cached_plan_failure?(pgerror)
|
|
478
522
|
pgerror.result.result_error_field(PG::PG_DIAG_SQLSTATE) == FEATURE_NOT_SUPPORTED &&
|
|
479
|
-
pgerror.result.result_error_field(PG::
|
|
523
|
+
pgerror.result.result_error_field(PG::PG_DIAG_MESSAGE_PRIMARY).include?(CACHED_PLAN_HEURISTIC)
|
|
480
524
|
rescue
|
|
481
525
|
false
|
|
482
526
|
end
|
data/lib/version.rb
CHANGED