From 6a2c255ebdd321044ba4e6fa62746d7b3efa86b1 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Fri, 14 Feb 2025 10:58:30 +0000 Subject: [PATCH 1/2] Add support for index include --- .../sqlserver/schema_creation.rb | 7 ++++ .../sqlserver/schema_statements.rb | 32 ++++++++++++++++++- .../connection_adapters/sqlserver_adapter.rb | 4 +++ 3 files changed, 42 insertions(+), 1 deletion(-) diff --git a/lib/active_record/connection_adapters/sqlserver/schema_creation.rb b/lib/active_record/connection_adapters/sqlserver/schema_creation.rb index 21479662f..866d2fab3 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_creation.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_creation.rb @@ -6,6 +6,8 @@ module SQLServer class SchemaCreation < SchemaCreation private + delegate :quoted_include_columns_for_index, to: :@conn + def supports_index_using? false end @@ -44,11 +46,16 @@ def visit_CreateIndexDefinition(o) sql << "INDEX" sql << "#{quote_column_name(index.name)} ON #{quote_table_name(index.table)}" sql << "(#{quoted_columns(index)})" + sql << "INCLUDE (#{quoted_include_columns(index.include)})" if supports_index_include? && index.include sql << "WHERE #{index.where}" if index.where sql.join(" ") end + def quoted_include_columns(o) + (String === o) ? o : quoted_include_columns_for_index(o) + end + def add_column_options!(sql, options) sql << " DEFAULT #{quote_default_expression_for_column_definition(options[:default], options[:column])}" if options_include_default?(options) if options[:collation].present? diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 9627a4fc5..c8382cc92 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -49,6 +49,7 @@ def indexes(table_name) name = index["index_name"] unique = index["index_description"].match?(/unique/) where = select_value("SELECT [filter_definition] FROM sys.indexes WHERE name = #{quote(name)}", "SCHEMA") + include_columns = index_include_columns(table_name, name) orders = {} columns = [] @@ -63,11 +64,31 @@ def indexes(table_name) columns << column end - indexes << IndexDefinition.new(table_name, name, unique, columns, where: where, orders: orders) + indexes << IndexDefinition.new(table_name, name, unique, columns, where: where, orders: orders, include: include_columns.presence) end end end + def index_include_columns(table_name, index_name) + sql = <<~SQL + SELECT + ic.index_id, + c.name AS column_name + FROM + sys.indexes i + JOIN + sys.index_columns ic ON i.object_id = ic.object_id AND i.index_id = ic.index_id + JOIN + sys.columns c ON ic.object_id = c.object_id AND ic.column_id = c.column_id + WHERE + i.object_id = OBJECT_ID('#{table_name}') + AND i.name = '#{index_name}' + AND ic.is_included_column = 1; + SQL + + select_all(sql, "SCHEMA").map { |row| row["column_name"] } + end + def columns(table_name) return [] if table_name.blank? @@ -421,6 +442,15 @@ def schema_names query_values(sql, "SCHEMA") end + def quoted_include_columns_for_index(column_names) # :nodoc: + return quote_column_name(column_names) if column_names.is_a?(Symbol) + + quoted_columns = column_names.each_with_object({}) do |name, result| + result[name.to_sym] = quote_column_name(name).dup + end + add_options_for_index_columns(quoted_columns).values.join(", ") + end + private def data_source_sql(name = nil, type: nil) diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index 39d0927fb..170f3286b 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -143,6 +143,10 @@ def supports_partial_index? true end + def supports_index_include? + true + end + def supports_expression_index? false end From 6bba3316a6debb299a6c19fe4eae308f35b13919 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Sat, 15 Feb 2025 16:51:28 +0000 Subject: [PATCH 2/2] Update CHANGELOG.md --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 856e2a85b..8c8b5e367 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ ## Unreleased +#### Added + +- [#1301](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1301) Add support for INDEX INCLUDE. + #### Changed - [#1273](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1273) TinyTDS v3+ is now required.