diff --git a/.rubocop.yml b/.rubocop.yml index 2546d6ab..5595fc0f 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -41,6 +41,6 @@ Style/WordArray: Style/RegexpLiteral: Enabled: false Metrics/MethodLength: - Max: 40 + Max: 60 Metrics/BlockLength: Max: 30 diff --git a/benchmarks/application.rb b/benchmarks/application.rb index e04692f5..a51c34ac 100644 --- a/benchmarks/application.rb +++ b/benchmarks/application.rb @@ -12,7 +12,7 @@ require_relative "models/album" class Application - def self.run # rubocop:disable Metrics/AbcSize, Metrics/MethodLength + def self.run # rubocop:disable Metrics/AbcSize ActiveRecord::Base.logger.level = Logger::WARN config = ActiveRecord::Base.connection_config spanner = Google::Cloud::Spanner.new project: config[:project], credentials: config[:credentials] diff --git a/examples/snippets/read-only-transactions/application.rb b/examples/snippets/read-only-transactions/application.rb index dea9c411..7d3839ea 100644 --- a/examples/snippets/read-only-transactions/application.rb +++ b/examples/snippets/read-only-transactions/application.rb @@ -10,7 +10,7 @@ require_relative "models/album" class Application - def self.run # rubocop:disable Metrics/AbcSize, Metrics/MethodLength + def self.run # rubocop:disable Metrics/AbcSize # Use a read-only transaction to execute multiple reads at the same commit timestamp. # The Spanner ActiveRecord adapter supports the custom isolation level :read_only that # will start a read-only Spanner transaction with a strong timestamp bound. diff --git a/lib/active_record/connection_adapters/spanner/schema_creation.rb b/lib/active_record/connection_adapters/spanner/schema_creation.rb index 69de16f0..1bda9d7c 100644 --- a/lib/active_record/connection_adapters/spanner/schema_creation.rb +++ b/lib/active_record/connection_adapters/spanner/schema_creation.rb @@ -11,7 +11,6 @@ class SchemaCreation < SchemaCreation private # rubocop:disable Naming/MethodName, Metrics/AbcSize, Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity - # rubocop:disable Metrics/MethodLength def visit_TableDefinition o create_sql = +"CREATE TABLE #{quote_table_name o.name} " @@ -133,7 +132,6 @@ def visit_IndexDefinition o end # rubocop:enable Naming/MethodName, Metrics/AbcSize, Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity - # rubocop:enable Metrics/MethodLength def add_column_options! column, sql, options if options[:null] == false || options[:primary_key] == true diff --git a/lib/active_record/connection_adapters/spanner/schema_statements.rb b/lib/active_record/connection_adapters/spanner/schema_statements.rb index a2a252a4..9bc49d53 100644 --- a/lib/active_record/connection_adapters/spanner/schema_statements.rb +++ b/lib/active_record/connection_adapters/spanner/schema_statements.rb @@ -40,6 +40,15 @@ def table_exists? table_name end alias data_source_exists? table_exists? + def extract_schema_qualified_name string + schema, name = string.to_s.scan(/[^`.\s]+|`[^`]*`/) + unless name + name = schema + schema = nil + end + [schema, name] + end + def create_table table_name, id: :primary_key, **options td = create_table_definition table_name, options diff --git a/lib/activerecord_spanner_adapter/foreign_key.rb b/lib/activerecord_spanner_adapter/foreign_key.rb index 8bc25e7e..ad52808a 100644 --- a/lib/activerecord_spanner_adapter/foreign_key.rb +++ b/lib/activerecord_spanner_adapter/foreign_key.rb @@ -6,7 +6,7 @@ module ActiveRecordSpannerAdapter class ForeignKey - attr_accessor :table_name, :name, :columns, :ref_table, :ref_columns, + attr_accessor :table_schema, :table_name, :name, :columns, :ref_schema, :ref_table, :ref_columns, :on_delete, :on_update def initialize \ @@ -16,10 +16,14 @@ def initialize \ ref_table, ref_columns, on_delete: nil, - on_update: nil + on_update: nil, + table_schema: "", + ref_schema: "" + @table_schema = table_schema @table_name = table_name @name = name @columns = Array(columns) + @ref_schema = ref_schema @ref_table = ref_table @ref_columns = Array(ref_columns) @on_delete = on_delete unless on_delete == "NO ACTION" diff --git a/lib/activerecord_spanner_adapter/index.rb b/lib/activerecord_spanner_adapter/index.rb index 5d4c1859..46de13af 100644 --- a/lib/activerecord_spanner_adapter/index.rb +++ b/lib/activerecord_spanner_adapter/index.rb @@ -8,7 +8,7 @@ module ActiveRecordSpannerAdapter class Index - attr_accessor :table, :name, :columns, :type, :unique, :null_filtered, + attr_accessor :schema, :table, :name, :columns, :type, :unique, :null_filtered, :interleave_in, :storing, :state def initialize \ @@ -20,7 +20,9 @@ def initialize \ null_filtered: false, interleave_in: nil, storing: nil, - state: nil + state: nil, + schema: "" + @schema = schema.to_s @table = table.to_s @name = name.to_s @columns = Array(columns) diff --git a/lib/activerecord_spanner_adapter/index/column.rb b/lib/activerecord_spanner_adapter/index/column.rb index 14587500..d297c0d2 100644 --- a/lib/activerecord_spanner_adapter/index/column.rb +++ b/lib/activerecord_spanner_adapter/index/column.rb @@ -7,16 +7,18 @@ module ActiveRecordSpannerAdapter class Index class Column - attr_accessor :table_name, :index_name, :name, :order, :ordinal_position + attr_accessor :table_name, :schema_name, :index_name, :name, :order, :ordinal_position def initialize \ table_name, index_name, name, + schema_name: "", order: nil, ordinal_position: nil @table_name = table_name.to_s @index_name = index_name.to_s + @schema_name = schema_name.to_s @name = name.to_s @order = order.to_s.upcase if order @ordinal_position = ordinal_position diff --git a/lib/activerecord_spanner_adapter/information_schema.rb b/lib/activerecord_spanner_adapter/information_schema.rb index 22a60e3b..db835664 100644 --- a/lib/activerecord_spanner_adapter/information_schema.rb +++ b/lib/activerecord_spanner_adapter/information_schema.rb @@ -24,7 +24,7 @@ def initialize connection @mutex = Mutex.new end - def tables table_name: nil, schema_name: nil, view: nil + def tables table_name: nil, schema_name: "", view: nil sql = +"SELECT TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME, PARENT_TABLE_NAME, ON_DELETE_ACTION" sql << " FROM INFORMATION_SCHEMA.TABLES" sql << " WHERE TABLE_SCHEMA=%s" @@ -45,17 +45,17 @@ def tables table_name: nil, schema_name: nil, view: nil ) if [:full, :columns].include? view - table.columns = table_columns table.name + table.columns = table_columns table.name, schema_name: schema_name end if [:full, :indexes].include? view - table.indexes = indexes table.name + table.indexes = indexes table.name, schema_name: table.schema_name end table end end - def table table_name, schema_name: nil, view: nil + def table table_name, schema_name: "", view: nil tables( table_name: table_name, schema_name: schema_name, @@ -63,26 +63,28 @@ def table table_name, schema_name: nil, view: nil ).first end - def table_columns table_name, column_name: nil + def table_columns table_name, column_name: nil, schema_name: "" primary_keys = table_primary_keys(table_name).map(&:name) sql = +"SELECT COLUMN_NAME, SPANNER_TYPE, IS_NULLABLE, GENERATION_EXPRESSION," sql << " CAST(COLUMN_DEFAULT AS STRING) AS COLUMN_DEFAULT, ORDINAL_POSITION" sql << " FROM INFORMATION_SCHEMA.COLUMNS" sql << " WHERE TABLE_NAME=%s" + sql << " AND TABLE_SCHEMA=%s" sql << " AND COLUMN_NAME=%s" if column_name sql << " ORDER BY ORDINAL_POSITION ASC" - column_options = column_options table_name, column_name + column_options = column_options table_name, column_name, schema_name: schema_name execute_query( sql, table_name: table_name, - column_name: column_name + column_name: column_name, + schema_name: schema_name ).map do |row| - _create_column table_name, row, primary_keys, column_options + _create_column table_name, row, primary_keys, column_options, schema_name: schema_name end end - def _create_column table_name, row, primary_keys, column_options + def _create_column table_name, row, primary_keys, column_options, schema_name: "" type, limit = parse_type_and_limit row["SPANNER_TYPE"] column_name = row["COLUMN_NAME"] options = column_options[column_name] @@ -104,6 +106,7 @@ def _create_column table_name, row, primary_keys, column_options table_name, column_name, type, + schema_name: schema_name, limit: limit, allow_commit_timestamp: options["allow_commit_timestamp"], ordinal_position: row["ORDINAL_POSITION"], @@ -114,8 +117,8 @@ def _create_column table_name, row, primary_keys, column_options primary_key: primary_key end - def table_column table_name, column_name - table_columns(table_name, column_name: column_name).first + def table_column table_name, column_name, schema_name: "" + table_columns(table_name, column_name: column_name, schema_name: schema_name).first end # Returns the primary key columns of the given table. By default it will only return the columns that are not part @@ -123,43 +126,49 @@ def table_column table_name, column_name # ActiveRecord. The parent primary key columns are filtered out by default to allow interleaved tables to be # considered as tables with a single-column primary key by ActiveRecord. The actual primary key of the table will # include both the parent primary key columns and the 'own' primary key columns of a table. - def table_primary_keys table_name, include_parent_keys = IsRails71OrLater + def table_primary_keys table_name, include_parent_keys = IsRails71OrLater, schema_name: "" sql = +"WITH TABLE_PK_COLS AS ( " - sql << "SELECT C.TABLE_NAME, C.COLUMN_NAME, C.INDEX_NAME, C.COLUMN_ORDERING, C.ORDINAL_POSITION " + sql << "SELECT C.TABLE_CATALOG, C.TABLE_SCHEMA, C.TABLE_NAME, C.COLUMN_NAME, " + sql << "C.INDEX_NAME, C.COLUMN_ORDERING, C.ORDINAL_POSITION " sql << "FROM INFORMATION_SCHEMA.INDEX_COLUMNS C " sql << "WHERE C.INDEX_TYPE = 'PRIMARY_KEY' " sql << "AND TABLE_CATALOG = '' " sql << "AND TABLE_SCHEMA = '') " sql << "SELECT INDEX_NAME, COLUMN_NAME, COLUMN_ORDERING, ORDINAL_POSITION " sql << "FROM TABLE_PK_COLS " - sql << "INNER JOIN INFORMATION_SCHEMA.TABLES T USING (TABLE_NAME) " + sql << "INNER JOIN INFORMATION_SCHEMA.TABLES T USING (TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME) " sql << "WHERE TABLE_NAME = %s " sql << "AND TABLE_CATALOG = '' " - sql << "AND TABLE_SCHEMA = '' " + sql << "AND TABLE_SCHEMA = %s " unless include_parent_keys sql << "AND (T.PARENT_TABLE_NAME IS NULL OR COLUMN_NAME NOT IN ( " sql << " SELECT COLUMN_NAME " sql << " FROM TABLE_PK_COLS " - sql << " WHERE TABLE_NAME = T.PARENT_TABLE_NAME " + sql << " WHERE TABLE_CATALOG = T.TABLE_CATALOG " + sql << " AND TABLE_SCHEMA=T.TABLE_SCHEMA " + sql << " AND TABLE_NAME = T.PARENT_TABLE_NAME " sql << ")) " end sql << "ORDER BY ORDINAL_POSITION" execute_query( sql, - table_name: table_name + table_name: table_name, + schema_name: schema_name ).map do |row| Index::Column.new \ table_name, row["INDEX_NAME"], row["COLUMN_NAME"], + schema_name: schema_name, order: row["COLUMN_ORDERING"], ordinal_position: row["ORDINAL_POSITION"] end end - def indexes table_name, index_name: nil, index_type: nil + def indexes table_name, schema_name: "", index_name: nil, index_type: nil table_indexes_columns = index_columns( table_name, + schema_name: schema_name, index_name: index_name ) @@ -167,7 +176,7 @@ def indexes table_name, index_name: nil, index_type: nil sql << " FROM INFORMATION_SCHEMA.INDEXES" sql << " WHERE TABLE_NAME=%s" sql << " AND TABLE_CATALOG = ''" - sql << " AND TABLE_SCHEMA = ''" + sql << " AND TABLE_SCHEMA = %s" sql << " AND INDEX_NAME=%s" if index_name sql << " AND INDEX_TYPE=%s" if index_type sql << " AND SPANNER_IS_MANAGED=FALSE" @@ -175,6 +184,7 @@ def indexes table_name, index_name: nil, index_type: nil execute_query( sql, table_name: table_name, + schema_name: schema_name, index_name: index_name, index_type: index_type ).map do |row| @@ -198,89 +208,120 @@ def indexes table_name, index_name: nil, index_type: nil null_filtered: row["IS_NULL_FILTERED"], interleave_in: row["PARENT_TABLE_NAME"], storing: storing, - state: row["INDEX_STATE"] + state: row["INDEX_STATE"], + schema: schema_name end end - def index table_name, index_name - indexes(table_name, index_name: index_name).first + def index table_name, index_name, schema_name: "" + indexes(table_name, index_name: index_name, schema_name: schema_name).first end - def index_columns table_name, index_name: nil + def index_columns table_name, schema_name: "", index_name: nil sql = +"SELECT INDEX_NAME, COLUMN_NAME, COLUMN_ORDERING, ORDINAL_POSITION" sql << " FROM INFORMATION_SCHEMA.INDEX_COLUMNS" sql << " WHERE TABLE_NAME=%s" sql << " AND TABLE_CATALOG = ''" - sql << " AND TABLE_SCHEMA = ''" + sql << " AND TABLE_SCHEMA = %s" sql << " AND INDEX_NAME=%s" if index_name sql << " ORDER BY ORDINAL_POSITION ASC" execute_query( sql, - table_name: table_name, index_name: index_name + table_name: table_name, schema_name: schema_name, index_name: index_name ).map do |row| Index::Column.new \ table_name, row["INDEX_NAME"], row["COLUMN_NAME"], + schema_name: schema_name, order: row["COLUMN_ORDERING"], ordinal_position: row["ORDINAL_POSITION"] end end - def indexes_by_columns table_name, column_names + def indexes_by_columns table_name, column_names, schema_name: "" column_names = Array(column_names).map(&:to_s) - indexes(table_name).select do |index| + indexes(table_name, schema_name: schema_name).select do |index| index.columns.any? { |c| column_names.include? c.name } end end - def foreign_keys table_name + def foreign_keys from_table_name, from_schema_name: "" sql = <<~SQL - SELECT cc.table_name AS to_table, - cc.column_name AS primary_key, - fk.column_name as column, - fk.constraint_name AS name, - rc.update_rule AS on_update, - rc.delete_rule AS on_delete - FROM information_schema.referential_constraints rc - INNER JOIN information_schema.key_column_usage fk ON rc.constraint_name = fk.constraint_name - INNER JOIN information_schema.constraint_column_usage cc ON rc.constraint_name = cc.constraint_name - WHERE fk.table_name = %s - AND fk.constraint_schema = %s + SELECT CONSTRAINT_CATALOG, CONSTRAINT_SCHEMA, CONSTRAINT_NAME, UPDATE_RULE, DELETE_RULE, + FK_CATALOG, FK_SCHEMA, FK_TABLE, + PK_CATALOG, PK_SCHEMA, PK_TABLE, + ARRAY_AGG(FK_COLUMN) AS FK_COLUMNS, ARRAY_AGG(PK_COLUMN) AS PK_COLUMNS + FROM (SELECT CONSTRAINTS.CONSTRAINT_CATALOG, + CONSTRAINTS.CONSTRAINT_SCHEMA, + CONSTRAINTS.CONSTRAINT_NAME, + CONSTRAINTS.UPDATE_RULE, + CONSTRAINTS.DELETE_RULE, + CHILD.TABLE_CATALOG AS FK_CATALOG, + CHILD.TABLE_SCHEMA AS FK_SCHEMA, + CHILD.TABLE_NAME AS FK_TABLE, + CHILD.COLUMN_NAME AS FK_COLUMN, + PARENT.TABLE_CATALOG AS PK_CATALOG, + PARENT.TABLE_SCHEMA AS PK_SCHEMA, + PARENT.TABLE_NAME AS PK_TABLE, + PARENT.COLUMN_NAME AS PK_COLUMN + FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS CONSTRAINTS + INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE CHILD + ON CONSTRAINTS.CONSTRAINT_CATALOG = CHILD.CONSTRAINT_CATALOG + AND CONSTRAINTS.CONSTRAINT_SCHEMA = CHILD.CONSTRAINT_SCHEMA + AND CONSTRAINTS.CONSTRAINT_NAME = CHILD.CONSTRAINT_NAME + INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE PARENT + ON CONSTRAINTS.UNIQUE_CONSTRAINT_CATALOG = PARENT.CONSTRAINT_CATALOG + AND CONSTRAINTS.UNIQUE_CONSTRAINT_SCHEMA = PARENT.CONSTRAINT_SCHEMA + AND CONSTRAINTS.UNIQUE_CONSTRAINT_NAME = PARENT.CONSTRAINT_NAME + AND PARENT.ORDINAL_POSITION = CHILD.POSITION_IN_UNIQUE_CONSTRAINT + ORDER BY CHILD.TABLE_CATALOG, CHILD.TABLE_SCHEMA, CHILD.TABLE_NAME, CHILD.POSITION_IN_UNIQUE_CONSTRAINT + ) FOREIGN_KEYS + WHERE FK_TABLE = %s + AND FK_SCHEMA = %s + GROUP BY CONSTRAINT_CATALOG, CONSTRAINT_SCHEMA, CONSTRAINT_NAME, UPDATE_RULE, DELETE_RULE, + FK_CATALOG, FK_SCHEMA, FK_TABLE, + PK_CATALOG, PK_SCHEMA, PK_TABLE SQL rows = execute_query( - sql, table_name: table_name, constraint_schema: "" + sql, table_name: from_table_name, constraint_schema: from_schema_name ) rows.map do |row| ForeignKey.new( - table_name, - row["name"], - row["column"], - row["to_table"], - row["primary_key"], - on_delete: row["on_delete"], - on_update: row["on_update"] + from_table_name, + row["CONSTRAINT_NAME"], + row["FK_COLUMNS"], + row["PK_TABLE"], + row["PK_COLUMNS"], + on_delete: row["DELETE_RULE"], + on_update: row["UPDATE_RULE"], + table_schema: from_schema_name, + ref_schema: row["PK_SCHEMA"] ) end end - def check_constraints table_name + def check_constraints table_name, schema_name: "" sql = <<~SQL.squish SELECT tc.TABLE_NAME, tc.CONSTRAINT_NAME, cc.CHECK_CLAUSE FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS tc - INNER JOIN INFORMATION_SCHEMA.CHECK_CONSTRAINTS cc ON tc.CONSTRAINT_NAME = cc.CONSTRAINT_NAME + INNER JOIN INFORMATION_SCHEMA.CHECK_CONSTRAINTS cc + ON tc.CONSTRAINT_CATALOG = cc.CONSTRAINT_CATALOG + AND tc.CONSTRAINT_SCHEMA = cc.CONSTRAINT_SCHEMA + AND tc.CONSTRAINT_NAME = cc.CONSTRAINT_NAME WHERE tc.TABLE_NAME = %s + AND tc.CONSTRAINT_SCHEMA = %s AND tc.CONSTRAINT_TYPE = 'CHECK' AND NOT (tc.CONSTRAINT_NAME LIKE 'CK_IS_NOT_NULL_%%' AND cc.CHECK_CLAUSE LIKE '%%IS NOT NULL') SQL - rows = execute_query sql, table_name: table_name + rows = execute_query sql, table_name: table_name, schema_name: schema_name rows.map do |row| ActiveRecord::ConnectionAdapters::CheckConstraintDefinition.new( @@ -363,16 +404,18 @@ def unescape_unicode value, start, length, base [value[start...(start + length)].to_i(base)].pack "U" end - def column_options table_name, column_name + def column_options table_name, column_name, schema_name: "" sql = +"SELECT COLUMN_NAME, OPTION_NAME, OPTION_TYPE, OPTION_VALUE" sql << " FROM INFORMATION_SCHEMA.COLUMN_OPTIONS" sql << " WHERE TABLE_NAME=%s" + sql << " AND TABLE_SCHEMA=%s" sql << " AND COLUMN_NAME=%s" if column_name column_options = Hash.new { |h, k| h[k] = {} } execute_query( sql, table_name: table_name, + schema_name: schema_name, column_name: column_name ).each_with_object(column_options) do |row, options| next unless row["OPTION_TYPE"] == "BOOL" diff --git a/lib/activerecord_spanner_adapter/table/column.rb b/lib/activerecord_spanner_adapter/table/column.rb index e2eba3ab..86bab72d 100644 --- a/lib/activerecord_spanner_adapter/table/column.rb +++ b/lib/activerecord_spanner_adapter/table/column.rb @@ -7,7 +7,7 @@ module ActiveRecordSpannerAdapter class Table class Column - attr_accessor :table_name, :name, :type, :limit, :ordinal_position, + attr_accessor :schema_name, :table_name, :name, :type, :limit, :ordinal_position, :allow_commit_timestamp, :default, :default_function, :generated, :primary_key, :nullable @@ -15,6 +15,7 @@ def initialize \ table_name, name, type, + schema_name: "", limit: nil, ordinal_position: nil, nullable: true, @@ -23,6 +24,7 @@ def initialize \ default_function: nil, generated: nil, primary_key: false + @schema_name = schema_name.to_s @table_name = table_name.to_s @name = name.to_s @type = type diff --git a/test/activerecord_spanner_adapter/information_schema_test.rb b/test/activerecord_spanner_adapter/information_schema_test.rb index 7e3eec17..020ddda6 100644 --- a/test/activerecord_spanner_adapter/information_schema_test.rb +++ b/test/activerecord_spanner_adapter/information_schema_test.rb @@ -131,15 +131,15 @@ def test_list_all_tables_with_columns_view set_mocked_result tables_schema_result info_schema.tables view: :columns pk_sql = is_7_1_or_higher? \ - ? "WITH TABLE_PK_COLS AS ( SELECT C.TABLE_NAME, C.COLUMN_NAME, C.INDEX_NAME, C.COLUMN_ORDERING, C.ORDINAL_POSITION FROM INFORMATION_SCHEMA.INDEX_COLUMNS C WHERE C.INDEX_TYPE = 'PRIMARY_KEY' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '') SELECT INDEX_NAME, COLUMN_NAME, COLUMN_ORDERING, ORDINAL_POSITION FROM TABLE_PK_COLS INNER JOIN INFORMATION_SCHEMA.TABLES T USING (TABLE_NAME) WHERE TABLE_NAME = 'accounts' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '' ORDER BY ORDINAL_POSITION" - : "WITH TABLE_PK_COLS AS ( SELECT C.TABLE_NAME, C.COLUMN_NAME, C.INDEX_NAME, C.COLUMN_ORDERING, C.ORDINAL_POSITION FROM INFORMATION_SCHEMA.INDEX_COLUMNS C WHERE C.INDEX_TYPE = 'PRIMARY_KEY' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '') SELECT INDEX_NAME, COLUMN_NAME, COLUMN_ORDERING, ORDINAL_POSITION FROM TABLE_PK_COLS INNER JOIN INFORMATION_SCHEMA.TABLES T USING (TABLE_NAME) WHERE TABLE_NAME = 'accounts' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '' AND (T.PARENT_TABLE_NAME IS NULL OR COLUMN_NAME NOT IN ( SELECT COLUMN_NAME FROM TABLE_PK_COLS WHERE TABLE_NAME = T.PARENT_TABLE_NAME )) ORDER BY ORDINAL_POSITION" + ? "WITH TABLE_PK_COLS AS ( SELECT C.TABLE_CATALOG, C.TABLE_SCHEMA, C.TABLE_NAME, C.COLUMN_NAME, C.INDEX_NAME, C.COLUMN_ORDERING, C.ORDINAL_POSITION FROM INFORMATION_SCHEMA.INDEX_COLUMNS C WHERE C.INDEX_TYPE = 'PRIMARY_KEY' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '') SELECT INDEX_NAME, COLUMN_NAME, COLUMN_ORDERING, ORDINAL_POSITION FROM TABLE_PK_COLS INNER JOIN INFORMATION_SCHEMA.TABLES T USING (TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME) WHERE TABLE_NAME = 'accounts' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '' ORDER BY ORDINAL_POSITION" + : "WITH TABLE_PK_COLS AS ( SELECT C.TABLE_CATALOG, C.TABLE_SCHEMA, C.TABLE_NAME, C.COLUMN_NAME, C.INDEX_NAME, C.COLUMN_ORDERING, C.ORDINAL_POSITION FROM INFORMATION_SCHEMA.INDEX_COLUMNS C WHERE C.INDEX_TYPE = 'PRIMARY_KEY' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '') SELECT INDEX_NAME, COLUMN_NAME, COLUMN_ORDERING, ORDINAL_POSITION FROM TABLE_PK_COLS INNER JOIN INFORMATION_SCHEMA.TABLES T USING (TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME) WHERE TABLE_NAME = 'accounts' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '' AND (T.PARENT_TABLE_NAME IS NULL OR COLUMN_NAME NOT IN ( SELECT COLUMN_NAME FROM TABLE_PK_COLS WHERE TABLE_CATALOG = T.TABLE_CATALOG AND TABLE_SCHEMA=T.TABLE_SCHEMA AND TABLE_NAME = T.PARENT_TABLE_NAME )) ORDER BY ORDINAL_POSITION" assert_sql_equal( [ "SELECT TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME, PARENT_TABLE_NAME, ON_DELETE_ACTION FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA=''", pk_sql, - "SELECT COLUMN_NAME, OPTION_NAME, OPTION_TYPE, OPTION_VALUE FROM INFORMATION_SCHEMA.COLUMN_OPTIONS WHERE TABLE_NAME='accounts'", - "SELECT COLUMN_NAME, SPANNER_TYPE, IS_NULLABLE, GENERATION_EXPRESSION, CAST(COLUMN_DEFAULT AS STRING) AS COLUMN_DEFAULT, ORDINAL_POSITION FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='accounts' ORDER BY ORDINAL_POSITION ASC" + "SELECT COLUMN_NAME, OPTION_NAME, OPTION_TYPE, OPTION_VALUE FROM INFORMATION_SCHEMA.COLUMN_OPTIONS WHERE TABLE_NAME='accounts' AND TABLE_SCHEMA=''", + "SELECT COLUMN_NAME, SPANNER_TYPE, IS_NULLABLE, GENERATION_EXPRESSION, CAST(COLUMN_DEFAULT AS STRING) AS COLUMN_DEFAULT, ORDINAL_POSITION FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='accounts' AND TABLE_SCHEMA='' ORDER BY ORDINAL_POSITION ASC" ], last_executed_sqls ) @@ -163,15 +163,15 @@ def test_list_all_tables_with_full_view set_mocked_result tables_schema_result info_schema.tables view: :full pk_sql = is_7_1_or_higher? \ - ? "WITH TABLE_PK_COLS AS ( SELECT C.TABLE_NAME, C.COLUMN_NAME, C.INDEX_NAME, C.COLUMN_ORDERING, C.ORDINAL_POSITION FROM INFORMATION_SCHEMA.INDEX_COLUMNS C WHERE C.INDEX_TYPE = 'PRIMARY_KEY' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '') SELECT INDEX_NAME, COLUMN_NAME, COLUMN_ORDERING, ORDINAL_POSITION FROM TABLE_PK_COLS INNER JOIN INFORMATION_SCHEMA.TABLES T USING (TABLE_NAME) WHERE TABLE_NAME = 'accounts' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '' ORDER BY ORDINAL_POSITION" - : "WITH TABLE_PK_COLS AS ( SELECT C.TABLE_NAME, C.COLUMN_NAME, C.INDEX_NAME, C.COLUMN_ORDERING, C.ORDINAL_POSITION FROM INFORMATION_SCHEMA.INDEX_COLUMNS C WHERE C.INDEX_TYPE = 'PRIMARY_KEY' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '') SELECT INDEX_NAME, COLUMN_NAME, COLUMN_ORDERING, ORDINAL_POSITION FROM TABLE_PK_COLS INNER JOIN INFORMATION_SCHEMA.TABLES T USING (TABLE_NAME) WHERE TABLE_NAME = 'accounts' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '' AND (T.PARENT_TABLE_NAME IS NULL OR COLUMN_NAME NOT IN ( SELECT COLUMN_NAME FROM TABLE_PK_COLS WHERE TABLE_NAME = T.PARENT_TABLE_NAME )) ORDER BY ORDINAL_POSITION" + ? "WITH TABLE_PK_COLS AS ( SELECT C.TABLE_CATALOG, C.TABLE_SCHEMA, C.TABLE_NAME, C.COLUMN_NAME, C.INDEX_NAME, C.COLUMN_ORDERING, C.ORDINAL_POSITION FROM INFORMATION_SCHEMA.INDEX_COLUMNS C WHERE C.INDEX_TYPE = 'PRIMARY_KEY' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '') SELECT INDEX_NAME, COLUMN_NAME, COLUMN_ORDERING, ORDINAL_POSITION FROM TABLE_PK_COLS INNER JOIN INFORMATION_SCHEMA.TABLES T USING (TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME) WHERE TABLE_NAME = 'accounts' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '' ORDER BY ORDINAL_POSITION" + : "WITH TABLE_PK_COLS AS ( SELECT C.TABLE_CATALOG, C.TABLE_SCHEMA, C.TABLE_NAME, C.COLUMN_NAME, C.INDEX_NAME, C.COLUMN_ORDERING, C.ORDINAL_POSITION FROM INFORMATION_SCHEMA.INDEX_COLUMNS C WHERE C.INDEX_TYPE = 'PRIMARY_KEY' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '') SELECT INDEX_NAME, COLUMN_NAME, COLUMN_ORDERING, ORDINAL_POSITION FROM TABLE_PK_COLS INNER JOIN INFORMATION_SCHEMA.TABLES T USING (TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME) WHERE TABLE_NAME = 'accounts' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '' AND (T.PARENT_TABLE_NAME IS NULL OR COLUMN_NAME NOT IN ( SELECT COLUMN_NAME FROM TABLE_PK_COLS WHERE TABLE_CATALOG = T.TABLE_CATALOG AND TABLE_SCHEMA=T.TABLE_SCHEMA AND TABLE_NAME = T.PARENT_TABLE_NAME )) ORDER BY ORDINAL_POSITION" assert_sql_equal( [ "SELECT TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME, PARENT_TABLE_NAME, ON_DELETE_ACTION FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA=''", pk_sql, - "SELECT COLUMN_NAME, OPTION_NAME, OPTION_TYPE, OPTION_VALUE FROM INFORMATION_SCHEMA.COLUMN_OPTIONS WHERE TABLE_NAME='accounts'", - "SELECT COLUMN_NAME, SPANNER_TYPE, IS_NULLABLE, GENERATION_EXPRESSION, CAST(COLUMN_DEFAULT AS STRING) AS COLUMN_DEFAULT, ORDINAL_POSITION FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='accounts' ORDER BY ORDINAL_POSITION ASC", + "SELECT COLUMN_NAME, OPTION_NAME, OPTION_TYPE, OPTION_VALUE FROM INFORMATION_SCHEMA.COLUMN_OPTIONS WHERE TABLE_NAME='accounts' AND TABLE_SCHEMA=''", + "SELECT COLUMN_NAME, SPANNER_TYPE, IS_NULLABLE, GENERATION_EXPRESSION, CAST(COLUMN_DEFAULT AS STRING) AS COLUMN_DEFAULT, ORDINAL_POSITION FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='accounts' AND TABLE_SCHEMA='' ORDER BY ORDINAL_POSITION ASC", "SELECT INDEX_NAME, COLUMN_NAME, COLUMN_ORDERING, ORDINAL_POSITION FROM INFORMATION_SCHEMA.INDEX_COLUMNS WHERE TABLE_NAME='accounts' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '' ORDER BY ORDINAL_POSITION ASC", "SELECT INDEX_NAME, INDEX_TYPE, IS_UNIQUE, IS_NULL_FILTERED, PARENT_TABLE_NAME, INDEX_STATE FROM INFORMATION_SCHEMA.INDEXES WHERE TABLE_NAME='accounts' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '' AND SPANNER_IS_MANAGED=FALSE" ], @@ -195,15 +195,15 @@ def test_table_with_columns_view info_schema.table "accounts", view: :columns pk_sql = is_7_1_or_higher? \ - ? "WITH TABLE_PK_COLS AS ( SELECT C.TABLE_NAME, C.COLUMN_NAME, C.INDEX_NAME, C.COLUMN_ORDERING, C.ORDINAL_POSITION FROM INFORMATION_SCHEMA.INDEX_COLUMNS C WHERE C.INDEX_TYPE = 'PRIMARY_KEY' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '') SELECT INDEX_NAME, COLUMN_NAME, COLUMN_ORDERING, ORDINAL_POSITION FROM TABLE_PK_COLS INNER JOIN INFORMATION_SCHEMA.TABLES T USING (TABLE_NAME) WHERE TABLE_NAME = 'accounts' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '' ORDER BY ORDINAL_POSITION" - : "WITH TABLE_PK_COLS AS ( SELECT C.TABLE_NAME, C.COLUMN_NAME, C.INDEX_NAME, C.COLUMN_ORDERING, C.ORDINAL_POSITION FROM INFORMATION_SCHEMA.INDEX_COLUMNS C WHERE C.INDEX_TYPE = 'PRIMARY_KEY' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '') SELECT INDEX_NAME, COLUMN_NAME, COLUMN_ORDERING, ORDINAL_POSITION FROM TABLE_PK_COLS INNER JOIN INFORMATION_SCHEMA.TABLES T USING (TABLE_NAME) WHERE TABLE_NAME = 'accounts' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '' AND (T.PARENT_TABLE_NAME IS NULL OR COLUMN_NAME NOT IN ( SELECT COLUMN_NAME FROM TABLE_PK_COLS WHERE TABLE_NAME = T.PARENT_TABLE_NAME )) ORDER BY ORDINAL_POSITION" + ? "WITH TABLE_PK_COLS AS ( SELECT C.TABLE_CATALOG, C.TABLE_SCHEMA, C.TABLE_NAME, C.COLUMN_NAME, C.INDEX_NAME, C.COLUMN_ORDERING, C.ORDINAL_POSITION FROM INFORMATION_SCHEMA.INDEX_COLUMNS C WHERE C.INDEX_TYPE = 'PRIMARY_KEY' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '') SELECT INDEX_NAME, COLUMN_NAME, COLUMN_ORDERING, ORDINAL_POSITION FROM TABLE_PK_COLS INNER JOIN INFORMATION_SCHEMA.TABLES T USING (TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME) WHERE TABLE_NAME = 'accounts' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '' ORDER BY ORDINAL_POSITION" + : "WITH TABLE_PK_COLS AS ( SELECT C.TABLE_CATALOG, C.TABLE_SCHEMA, C.TABLE_NAME, C.COLUMN_NAME, C.INDEX_NAME, C.COLUMN_ORDERING, C.ORDINAL_POSITION FROM INFORMATION_SCHEMA.INDEX_COLUMNS C WHERE C.INDEX_TYPE = 'PRIMARY_KEY' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '') SELECT INDEX_NAME, COLUMN_NAME, COLUMN_ORDERING, ORDINAL_POSITION FROM TABLE_PK_COLS INNER JOIN INFORMATION_SCHEMA.TABLES T USING (TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME) WHERE TABLE_NAME = 'accounts' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '' AND (T.PARENT_TABLE_NAME IS NULL OR COLUMN_NAME NOT IN ( SELECT COLUMN_NAME FROM TABLE_PK_COLS WHERE TABLE_CATALOG = T.TABLE_CATALOG AND TABLE_SCHEMA=T.TABLE_SCHEMA AND TABLE_NAME = T.PARENT_TABLE_NAME )) ORDER BY ORDINAL_POSITION" assert_sql_equal( [ "SELECT TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME, PARENT_TABLE_NAME, ON_DELETE_ACTION FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA='' AND TABLE_NAME='accounts'", pk_sql, - "SELECT COLUMN_NAME, OPTION_NAME, OPTION_TYPE, OPTION_VALUE FROM INFORMATION_SCHEMA.COLUMN_OPTIONS WHERE TABLE_NAME='accounts'", - "SELECT COLUMN_NAME, SPANNER_TYPE, IS_NULLABLE, GENERATION_EXPRESSION, CAST(COLUMN_DEFAULT AS STRING) AS COLUMN_DEFAULT, ORDINAL_POSITION FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='accounts' ORDER BY ORDINAL_POSITION ASC" + "SELECT COLUMN_NAME, OPTION_NAME, OPTION_TYPE, OPTION_VALUE FROM INFORMATION_SCHEMA.COLUMN_OPTIONS WHERE TABLE_NAME='accounts' AND TABLE_SCHEMA=''", + "SELECT COLUMN_NAME, SPANNER_TYPE, IS_NULLABLE, GENERATION_EXPRESSION, CAST(COLUMN_DEFAULT AS STRING) AS COLUMN_DEFAULT, ORDINAL_POSITION FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='accounts' AND TABLE_SCHEMA='' ORDER BY ORDINAL_POSITION ASC" ], last_executed_sqls ) @@ -227,15 +227,15 @@ def test_get_table_with_full_view set_mocked_result tables_schema_result info_schema.table "accounts", view: :full pk_sql = is_7_1_or_higher? \ - ? "WITH TABLE_PK_COLS AS ( SELECT C.TABLE_NAME, C.COLUMN_NAME, C.INDEX_NAME, C.COLUMN_ORDERING, C.ORDINAL_POSITION FROM INFORMATION_SCHEMA.INDEX_COLUMNS C WHERE C.INDEX_TYPE = 'PRIMARY_KEY' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '') SELECT INDEX_NAME, COLUMN_NAME, COLUMN_ORDERING, ORDINAL_POSITION FROM TABLE_PK_COLS INNER JOIN INFORMATION_SCHEMA.TABLES T USING (TABLE_NAME) WHERE TABLE_NAME = 'accounts' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '' ORDER BY ORDINAL_POSITION" - : "WITH TABLE_PK_COLS AS ( SELECT C.TABLE_NAME, C.COLUMN_NAME, C.INDEX_NAME, C.COLUMN_ORDERING, C.ORDINAL_POSITION FROM INFORMATION_SCHEMA.INDEX_COLUMNS C WHERE C.INDEX_TYPE = 'PRIMARY_KEY' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '') SELECT INDEX_NAME, COLUMN_NAME, COLUMN_ORDERING, ORDINAL_POSITION FROM TABLE_PK_COLS INNER JOIN INFORMATION_SCHEMA.TABLES T USING (TABLE_NAME) WHERE TABLE_NAME = 'accounts' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '' AND (T.PARENT_TABLE_NAME IS NULL OR COLUMN_NAME NOT IN ( SELECT COLUMN_NAME FROM TABLE_PK_COLS WHERE TABLE_NAME = T.PARENT_TABLE_NAME )) ORDER BY ORDINAL_POSITION" + ? "WITH TABLE_PK_COLS AS ( SELECT C.TABLE_CATALOG, C.TABLE_SCHEMA, C.TABLE_NAME, C.COLUMN_NAME, C.INDEX_NAME, C.COLUMN_ORDERING, C.ORDINAL_POSITION FROM INFORMATION_SCHEMA.INDEX_COLUMNS C WHERE C.INDEX_TYPE = 'PRIMARY_KEY' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '') SELECT INDEX_NAME, COLUMN_NAME, COLUMN_ORDERING, ORDINAL_POSITION FROM TABLE_PK_COLS INNER JOIN INFORMATION_SCHEMA.TABLES T USING (TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME) WHERE TABLE_NAME = 'accounts' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '' ORDER BY ORDINAL_POSITION" + : "WITH TABLE_PK_COLS AS ( SELECT C.TABLE_CATALOG, C.TABLE_SCHEMA, C.TABLE_NAME, C.COLUMN_NAME, C.INDEX_NAME, C.COLUMN_ORDERING, C.ORDINAL_POSITION FROM INFORMATION_SCHEMA.INDEX_COLUMNS C WHERE C.INDEX_TYPE = 'PRIMARY_KEY' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '') SELECT INDEX_NAME, COLUMN_NAME, COLUMN_ORDERING, ORDINAL_POSITION FROM TABLE_PK_COLS INNER JOIN INFORMATION_SCHEMA.TABLES T USING (TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME) WHERE TABLE_NAME = 'accounts' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '' AND (T.PARENT_TABLE_NAME IS NULL OR COLUMN_NAME NOT IN ( SELECT COLUMN_NAME FROM TABLE_PK_COLS WHERE TABLE_CATALOG = T.TABLE_CATALOG AND TABLE_SCHEMA=T.TABLE_SCHEMA AND TABLE_NAME = T.PARENT_TABLE_NAME )) ORDER BY ORDINAL_POSITION" assert_sql_equal( [ "SELECT TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME, PARENT_TABLE_NAME, ON_DELETE_ACTION FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA='' AND TABLE_NAME='accounts'", pk_sql, - "SELECT COLUMN_NAME, OPTION_NAME, OPTION_TYPE, OPTION_VALUE FROM INFORMATION_SCHEMA.COLUMN_OPTIONS WHERE TABLE_NAME='accounts'", - "SELECT COLUMN_NAME, SPANNER_TYPE, IS_NULLABLE, GENERATION_EXPRESSION, CAST(COLUMN_DEFAULT AS STRING) AS COLUMN_DEFAULT, ORDINAL_POSITION FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='accounts' ORDER BY ORDINAL_POSITION ASC", + "SELECT COLUMN_NAME, OPTION_NAME, OPTION_TYPE, OPTION_VALUE FROM INFORMATION_SCHEMA.COLUMN_OPTIONS WHERE TABLE_NAME='accounts' AND TABLE_SCHEMA=''", + "SELECT COLUMN_NAME, SPANNER_TYPE, IS_NULLABLE, GENERATION_EXPRESSION, CAST(COLUMN_DEFAULT AS STRING) AS COLUMN_DEFAULT, ORDINAL_POSITION FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='accounts' AND TABLE_SCHEMA='' ORDER BY ORDINAL_POSITION ASC", "SELECT INDEX_NAME, COLUMN_NAME, COLUMN_ORDERING, ORDINAL_POSITION FROM INFORMATION_SCHEMA.INDEX_COLUMNS WHERE TABLE_NAME='accounts' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '' ORDER BY ORDINAL_POSITION ASC", "SELECT INDEX_NAME, INDEX_TYPE, IS_UNIQUE, IS_NULL_FILTERED, PARENT_TABLE_NAME, INDEX_STATE FROM INFORMATION_SCHEMA.INDEXES WHERE TABLE_NAME='accounts' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '' AND SPANNER_IS_MANAGED=FALSE" ], @@ -250,14 +250,14 @@ def test_list_table_columns result = info_schema.table_columns "accounts" assert_equal result.length, 2 pk_sql = is_7_1_or_higher? \ - ? "WITH TABLE_PK_COLS AS ( SELECT C.TABLE_NAME, C.COLUMN_NAME, C.INDEX_NAME, C.COLUMN_ORDERING, C.ORDINAL_POSITION FROM INFORMATION_SCHEMA.INDEX_COLUMNS C WHERE C.INDEX_TYPE = 'PRIMARY_KEY' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '') SELECT INDEX_NAME, COLUMN_NAME, COLUMN_ORDERING, ORDINAL_POSITION FROM TABLE_PK_COLS INNER JOIN INFORMATION_SCHEMA.TABLES T USING (TABLE_NAME) WHERE TABLE_NAME = 'accounts' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '' ORDER BY ORDINAL_POSITION" - : "WITH TABLE_PK_COLS AS ( SELECT C.TABLE_NAME, C.COLUMN_NAME, C.INDEX_NAME, C.COLUMN_ORDERING, C.ORDINAL_POSITION FROM INFORMATION_SCHEMA.INDEX_COLUMNS C WHERE C.INDEX_TYPE = 'PRIMARY_KEY' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '') SELECT INDEX_NAME, COLUMN_NAME, COLUMN_ORDERING, ORDINAL_POSITION FROM TABLE_PK_COLS INNER JOIN INFORMATION_SCHEMA.TABLES T USING (TABLE_NAME) WHERE TABLE_NAME = 'accounts' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '' AND (T.PARENT_TABLE_NAME IS NULL OR COLUMN_NAME NOT IN ( SELECT COLUMN_NAME FROM TABLE_PK_COLS WHERE TABLE_NAME = T.PARENT_TABLE_NAME )) ORDER BY ORDINAL_POSITION" + ? "WITH TABLE_PK_COLS AS ( SELECT C.TABLE_CATALOG, C.TABLE_SCHEMA, C.TABLE_NAME, C.COLUMN_NAME, C.INDEX_NAME, C.COLUMN_ORDERING, C.ORDINAL_POSITION FROM INFORMATION_SCHEMA.INDEX_COLUMNS C WHERE C.INDEX_TYPE = 'PRIMARY_KEY' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '') SELECT INDEX_NAME, COLUMN_NAME, COLUMN_ORDERING, ORDINAL_POSITION FROM TABLE_PK_COLS INNER JOIN INFORMATION_SCHEMA.TABLES T USING (TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME) WHERE TABLE_NAME = 'accounts' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '' ORDER BY ORDINAL_POSITION" + : "WITH TABLE_PK_COLS AS ( SELECT C.TABLE_CATALOG, C.TABLE_SCHEMA, C.TABLE_NAME, C.COLUMN_NAME, C.INDEX_NAME, C.COLUMN_ORDERING, C.ORDINAL_POSITION FROM INFORMATION_SCHEMA.INDEX_COLUMNS C WHERE C.INDEX_TYPE = 'PRIMARY_KEY' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '') SELECT INDEX_NAME, COLUMN_NAME, COLUMN_ORDERING, ORDINAL_POSITION FROM TABLE_PK_COLS INNER JOIN INFORMATION_SCHEMA.TABLES T USING (TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME) WHERE TABLE_NAME = 'accounts' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '' AND (T.PARENT_TABLE_NAME IS NULL OR COLUMN_NAME NOT IN ( SELECT COLUMN_NAME FROM TABLE_PK_COLS WHERE TABLE_CATALOG = T.TABLE_CATALOG AND TABLE_SCHEMA=T.TABLE_SCHEMA AND TABLE_NAME = T.PARENT_TABLE_NAME )) ORDER BY ORDINAL_POSITION" assert_sql_equal( [ pk_sql, - "SELECT COLUMN_NAME, OPTION_NAME, OPTION_TYPE, OPTION_VALUE FROM INFORMATION_SCHEMA.COLUMN_OPTIONS WHERE TABLE_NAME='accounts'", - "SELECT COLUMN_NAME, SPANNER_TYPE, IS_NULLABLE, GENERATION_EXPRESSION, CAST(COLUMN_DEFAULT AS STRING) AS COLUMN_DEFAULT, ORDINAL_POSITION FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='accounts' ORDER BY ORDINAL_POSITION ASC" + "SELECT COLUMN_NAME, OPTION_NAME, OPTION_TYPE, OPTION_VALUE FROM INFORMATION_SCHEMA.COLUMN_OPTIONS WHERE TABLE_NAME='accounts' AND TABLE_SCHEMA=''", + "SELECT COLUMN_NAME, SPANNER_TYPE, IS_NULLABLE, GENERATION_EXPRESSION, CAST(COLUMN_DEFAULT AS STRING) AS COLUMN_DEFAULT, ORDINAL_POSITION FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='accounts' AND TABLE_SCHEMA='' ORDER BY ORDINAL_POSITION ASC" ], last_executed_sqls ) @@ -290,14 +290,14 @@ def test_get_table_column_with_options result = info_schema.table_columns "accounts" assert_equal result.length, 2 pk_sql = is_7_1_or_higher? \ - ? "WITH TABLE_PK_COLS AS ( SELECT C.TABLE_NAME, C.COLUMN_NAME, C.INDEX_NAME, C.COLUMN_ORDERING, C.ORDINAL_POSITION FROM INFORMATION_SCHEMA.INDEX_COLUMNS C WHERE C.INDEX_TYPE = 'PRIMARY_KEY' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '') SELECT INDEX_NAME, COLUMN_NAME, COLUMN_ORDERING, ORDINAL_POSITION FROM TABLE_PK_COLS INNER JOIN INFORMATION_SCHEMA.TABLES T USING (TABLE_NAME) WHERE TABLE_NAME = 'accounts' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '' ORDER BY ORDINAL_POSITION" - : "WITH TABLE_PK_COLS AS ( SELECT C.TABLE_NAME, C.COLUMN_NAME, C.INDEX_NAME, C.COLUMN_ORDERING, C.ORDINAL_POSITION FROM INFORMATION_SCHEMA.INDEX_COLUMNS C WHERE C.INDEX_TYPE = 'PRIMARY_KEY' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '') SELECT INDEX_NAME, COLUMN_NAME, COLUMN_ORDERING, ORDINAL_POSITION FROM TABLE_PK_COLS INNER JOIN INFORMATION_SCHEMA.TABLES T USING (TABLE_NAME) WHERE TABLE_NAME = 'accounts' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '' AND (T.PARENT_TABLE_NAME IS NULL OR COLUMN_NAME NOT IN ( SELECT COLUMN_NAME FROM TABLE_PK_COLS WHERE TABLE_NAME = T.PARENT_TABLE_NAME )) ORDER BY ORDINAL_POSITION" + ? "WITH TABLE_PK_COLS AS ( SELECT C.TABLE_CATALOG, C.TABLE_SCHEMA, C.TABLE_NAME, C.COLUMN_NAME, C.INDEX_NAME, C.COLUMN_ORDERING, C.ORDINAL_POSITION FROM INFORMATION_SCHEMA.INDEX_COLUMNS C WHERE C.INDEX_TYPE = 'PRIMARY_KEY' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '') SELECT INDEX_NAME, COLUMN_NAME, COLUMN_ORDERING, ORDINAL_POSITION FROM TABLE_PK_COLS INNER JOIN INFORMATION_SCHEMA.TABLES T USING (TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME) WHERE TABLE_NAME = 'accounts' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '' ORDER BY ORDINAL_POSITION" + : "WITH TABLE_PK_COLS AS ( SELECT C.TABLE_CATALOG, C.TABLE_SCHEMA, C.TABLE_NAME, C.COLUMN_NAME, C.INDEX_NAME, C.COLUMN_ORDERING, C.ORDINAL_POSITION FROM INFORMATION_SCHEMA.INDEX_COLUMNS C WHERE C.INDEX_TYPE = 'PRIMARY_KEY' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '') SELECT INDEX_NAME, COLUMN_NAME, COLUMN_ORDERING, ORDINAL_POSITION FROM TABLE_PK_COLS INNER JOIN INFORMATION_SCHEMA.TABLES T USING (TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME) WHERE TABLE_NAME = 'accounts' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '' AND (T.PARENT_TABLE_NAME IS NULL OR COLUMN_NAME NOT IN ( SELECT COLUMN_NAME FROM TABLE_PK_COLS WHERE TABLE_CATALOG = T.TABLE_CATALOG AND TABLE_SCHEMA=T.TABLE_SCHEMA AND TABLE_NAME = T.PARENT_TABLE_NAME )) ORDER BY ORDINAL_POSITION" assert_sql_equal( [ pk_sql, - "SELECT COLUMN_NAME, OPTION_NAME, OPTION_TYPE, OPTION_VALUE FROM INFORMATION_SCHEMA.COLUMN_OPTIONS WHERE TABLE_NAME='accounts'", - "SELECT COLUMN_NAME, SPANNER_TYPE, IS_NULLABLE, GENERATION_EXPRESSION, CAST(COLUMN_DEFAULT AS STRING) AS COLUMN_DEFAULT, ORDINAL_POSITION FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='accounts' ORDER BY ORDINAL_POSITION ASC" + "SELECT COLUMN_NAME, OPTION_NAME, OPTION_TYPE, OPTION_VALUE FROM INFORMATION_SCHEMA.COLUMN_OPTIONS WHERE TABLE_NAME='accounts' AND TABLE_SCHEMA=''", + "SELECT COLUMN_NAME, SPANNER_TYPE, IS_NULLABLE, GENERATION_EXPRESSION, CAST(COLUMN_DEFAULT AS STRING) AS COLUMN_DEFAULT, ORDINAL_POSITION FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='accounts' AND TABLE_SCHEMA='' ORDER BY ORDINAL_POSITION ASC" ], last_executed_sqls ) @@ -326,14 +326,14 @@ def test_get_table_column column = info_schema.table_column "accounts", "account_id" assert_instance_of ActiveRecordSpannerAdapter::Table::Column, column pk_sql = is_7_1_or_higher? \ - ? "WITH TABLE_PK_COLS AS ( SELECT C.TABLE_NAME, C.COLUMN_NAME, C.INDEX_NAME, C.COLUMN_ORDERING, C.ORDINAL_POSITION FROM INFORMATION_SCHEMA.INDEX_COLUMNS C WHERE C.INDEX_TYPE = 'PRIMARY_KEY' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '') SELECT INDEX_NAME, COLUMN_NAME, COLUMN_ORDERING, ORDINAL_POSITION FROM TABLE_PK_COLS INNER JOIN INFORMATION_SCHEMA.TABLES T USING (TABLE_NAME) WHERE TABLE_NAME = 'accounts' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '' ORDER BY ORDINAL_POSITION" - : "WITH TABLE_PK_COLS AS ( SELECT C.TABLE_NAME, C.COLUMN_NAME, C.INDEX_NAME, C.COLUMN_ORDERING, C.ORDINAL_POSITION FROM INFORMATION_SCHEMA.INDEX_COLUMNS C WHERE C.INDEX_TYPE = 'PRIMARY_KEY' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '') SELECT INDEX_NAME, COLUMN_NAME, COLUMN_ORDERING, ORDINAL_POSITION FROM TABLE_PK_COLS INNER JOIN INFORMATION_SCHEMA.TABLES T USING (TABLE_NAME) WHERE TABLE_NAME = 'accounts' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '' AND (T.PARENT_TABLE_NAME IS NULL OR COLUMN_NAME NOT IN ( SELECT COLUMN_NAME FROM TABLE_PK_COLS WHERE TABLE_NAME = T.PARENT_TABLE_NAME )) ORDER BY ORDINAL_POSITION" + ? "WITH TABLE_PK_COLS AS ( SELECT C.TABLE_CATALOG, C.TABLE_SCHEMA, C.TABLE_NAME, C.COLUMN_NAME, C.INDEX_NAME, C.COLUMN_ORDERING, C.ORDINAL_POSITION FROM INFORMATION_SCHEMA.INDEX_COLUMNS C WHERE C.INDEX_TYPE = 'PRIMARY_KEY' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '') SELECT INDEX_NAME, COLUMN_NAME, COLUMN_ORDERING, ORDINAL_POSITION FROM TABLE_PK_COLS INNER JOIN INFORMATION_SCHEMA.TABLES T USING (TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME) WHERE TABLE_NAME = 'accounts' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '' ORDER BY ORDINAL_POSITION" + : "WITH TABLE_PK_COLS AS ( SELECT C.TABLE_CATALOG, C.TABLE_SCHEMA, C.TABLE_NAME, C.COLUMN_NAME, C.INDEX_NAME, C.COLUMN_ORDERING, C.ORDINAL_POSITION FROM INFORMATION_SCHEMA.INDEX_COLUMNS C WHERE C.INDEX_TYPE = 'PRIMARY_KEY' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '') SELECT INDEX_NAME, COLUMN_NAME, COLUMN_ORDERING, ORDINAL_POSITION FROM TABLE_PK_COLS INNER JOIN INFORMATION_SCHEMA.TABLES T USING (TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME) WHERE TABLE_NAME = 'accounts' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '' AND (T.PARENT_TABLE_NAME IS NULL OR COLUMN_NAME NOT IN ( SELECT COLUMN_NAME FROM TABLE_PK_COLS WHERE TABLE_CATALOG = T.TABLE_CATALOG AND TABLE_SCHEMA=T.TABLE_SCHEMA AND TABLE_NAME = T.PARENT_TABLE_NAME )) ORDER BY ORDINAL_POSITION" assert_sql_equal( [ pk_sql, - "SELECT COLUMN_NAME, OPTION_NAME, OPTION_TYPE, OPTION_VALUE FROM INFORMATION_SCHEMA.COLUMN_OPTIONS WHERE TABLE_NAME='accounts' AND COLUMN_NAME='account_id'", - "SELECT COLUMN_NAME, SPANNER_TYPE, IS_NULLABLE, GENERATION_EXPRESSION, CAST(COLUMN_DEFAULT AS STRING) AS COLUMN_DEFAULT, ORDINAL_POSITION FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='accounts' AND COLUMN_NAME='account_id' ORDER BY ORDINAL_POSITION ASC" + "SELECT COLUMN_NAME, OPTION_NAME, OPTION_TYPE, OPTION_VALUE FROM INFORMATION_SCHEMA.COLUMN_OPTIONS WHERE TABLE_NAME='accounts' AND TABLE_SCHEMA='' AND COLUMN_NAME='account_id'", + "SELECT COLUMN_NAME, SPANNER_TYPE, IS_NULLABLE, GENERATION_EXPRESSION, CAST(COLUMN_DEFAULT AS STRING) AS COLUMN_DEFAULT, ORDINAL_POSITION FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='accounts' AND TABLE_SCHEMA='' AND COLUMN_NAME='account_id' ORDER BY ORDINAL_POSITION ASC" ], last_executed_sqls ) @@ -486,7 +486,7 @@ def test_check_constraints assert_equal results.length, 1 assert_sql_equal( - "SELECT tc.TABLE_NAME, tc.CONSTRAINT_NAME, cc.CHECK_CLAUSE FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS tc INNER JOIN INFORMATION_SCHEMA.CHECK_CONSTRAINTS cc ON tc.CONSTRAINT_NAME = cc.CONSTRAINT_NAME WHERE tc.TABLE_NAME = 'accounts' AND tc.CONSTRAINT_TYPE = 'CHECK' AND NOT (tc.CONSTRAINT_NAME LIKE 'CK_IS_NOT_NULL_%' AND cc.CHECK_CLAUSE LIKE '%IS NOT NULL')", + "SELECT tc.TABLE_NAME, tc.CONSTRAINT_NAME, cc.CHECK_CLAUSE FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS tc INNER JOIN INFORMATION_SCHEMA.CHECK_CONSTRAINTS cc ON tc.CONSTRAINT_CATALOG = cc.CONSTRAINT_CATALOG AND tc.CONSTRAINT_SCHEMA = cc.CONSTRAINT_SCHEMA AND tc.CONSTRAINT_NAME = cc.CONSTRAINT_NAME WHERE tc.TABLE_NAME = 'accounts' AND tc.CONSTRAINT_SCHEMA = '' AND tc.CONSTRAINT_TYPE = 'CHECK' AND NOT (tc.CONSTRAINT_NAME LIKE 'CK_IS_NOT_NULL_%' AND cc.CHECK_CLAUSE LIKE '%IS NOT NULL')", last_executed_sql ) diff --git a/test/activerecord_spanner_interleaved_table/model_helper.rb b/test/activerecord_spanner_interleaved_table/model_helper.rb index 66b024fe..f66c1b6d 100644 --- a/test/activerecord_spanner_interleaved_table/model_helper.rb +++ b/test/activerecord_spanner_interleaved_table/model_helper.rb @@ -11,6 +11,7 @@ return if ActiveRecord::gem_version >= Gem::Version.create('7.1.0') +require_relative "../activerecord_spanner_mock_server/model_helper" require_relative "models/singer" require_relative "models/album" @@ -151,9 +152,9 @@ def self.register_select_tables_result spanner_mock_server end def self.register_singers_columns_result spanner_mock_server - register_commit_timestamps_result spanner_mock_server, "singers" + MockServerTests.register_commit_timestamps_result spanner_mock_server, "singers" - sql = "SELECT COLUMN_NAME, SPANNER_TYPE, IS_NULLABLE, GENERATION_EXPRESSION, CAST(COLUMN_DEFAULT AS STRING) AS COLUMN_DEFAULT, ORDINAL_POSITION FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='singers' ORDER BY ORDINAL_POSITION ASC" + sql = MockServerTests.table_columns_sql "singers" column_name = Field.new name: "COLUMN_NAME", type: Type.new(code: TypeCode::STRING) spanner_type = Field.new name: "SPANNER_TYPE", type: Type.new(code: TypeCode::STRING) @@ -200,31 +201,8 @@ def self.register_singers_columns_result spanner_mock_server spanner_mock_server.put_statement_result sql, StatementResult.new(result_set) end - def self.register_commit_timestamps_result spanner_mock_server, table_name, column_name = nil, commit_timestamps_col = nil - option_sql = +"SELECT COLUMN_NAME, OPTION_NAME, OPTION_TYPE, OPTION_VALUE FROM INFORMATION_SCHEMA.COLUMN_OPTIONS WHERE TABLE_NAME='#{table_name}'" - option_sql << " AND COLUMN_NAME='#{column_name}'" if column_name - column_name = Field.new name: "COLUMN_NAME", type: Type.new(code: TypeCode::STRING) - option_name = Field.new name: "OPTION_NAME", type: Type.new(code: TypeCode::STRING) - option_type = Field.new name: "OPTION_TYPE", type: Type.new(code: TypeCode::STRING) - option_value = Field.new name: "OPTION_VALUE", type: Type.new(code: TypeCode::STRING) - metadata = ResultSetMetadata.new row_type: StructType.new - metadata.row_type.fields.push column_name, option_name, option_type, option_value - result_set = ResultSet.new metadata: metadata - row = ListValue.new - if commit_timestamps_col - row.values.push( - Value.new(string_value: commit_timestamps_col), - Value.new(string_value: "allow_commit_timestamp"), - Value.new(string_value: "BOOL"), - Value.new(string_value: "TRUE"), - ) - end - result_set.rows.push row - spanner_mock_server.put_statement_result option_sql, StatementResult.new(result_set) - end - def self.register_singers_primary_key_columns_result spanner_mock_server - sql = "WITH TABLE_PK_COLS AS ( SELECT C.TABLE_NAME, C.COLUMN_NAME, C.INDEX_NAME, C.COLUMN_ORDERING, C.ORDINAL_POSITION FROM INFORMATION_SCHEMA.INDEX_COLUMNS C WHERE C.INDEX_TYPE = 'PRIMARY_KEY' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '') SELECT INDEX_NAME, COLUMN_NAME, COLUMN_ORDERING, ORDINAL_POSITION FROM TABLE_PK_COLS INNER JOIN INFORMATION_SCHEMA.TABLES T USING (TABLE_NAME) WHERE TABLE_NAME = 'singers' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '' AND (T.PARENT_TABLE_NAME IS NULL OR COLUMN_NAME NOT IN ( SELECT COLUMN_NAME FROM TABLE_PK_COLS WHERE TABLE_NAME = T.PARENT_TABLE_NAME )) ORDER BY ORDINAL_POSITION" + sql = MockServerTests.primary_key_columns_sql "singers", parent_keys: false register_singers_key_columns_result spanner_mock_server, sql end @@ -256,9 +234,9 @@ def self.register_singers_key_columns_result spanner_mock_server, sql end def self.register_albums_columns_result spanner_mock_server - register_commit_timestamps_result spanner_mock_server, "albums" + MockServerTests.register_commit_timestamps_result spanner_mock_server, "albums" - sql = "SELECT COLUMN_NAME, SPANNER_TYPE, IS_NULLABLE, GENERATION_EXPRESSION, CAST(COLUMN_DEFAULT AS STRING) AS COLUMN_DEFAULT, ORDINAL_POSITION FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='albums' ORDER BY ORDINAL_POSITION ASC" + sql = MockServerTests.table_columns_sql "albums" column_name = Field.new name: "COLUMN_NAME", type: Type.new(code: TypeCode::STRING) spanner_type = Field.new name: "SPANNER_TYPE", type: Type.new(code: TypeCode::STRING) @@ -306,12 +284,12 @@ def self.register_albums_columns_result spanner_mock_server end def self.register_albums_primary_key_columns_result spanner_mock_server - sql = "WITH TABLE_PK_COLS AS ( SELECT C.TABLE_NAME, C.COLUMN_NAME, C.INDEX_NAME, C.COLUMN_ORDERING, C.ORDINAL_POSITION FROM INFORMATION_SCHEMA.INDEX_COLUMNS C WHERE C.INDEX_TYPE = 'PRIMARY_KEY' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '') SELECT INDEX_NAME, COLUMN_NAME, COLUMN_ORDERING, ORDINAL_POSITION FROM TABLE_PK_COLS INNER JOIN INFORMATION_SCHEMA.TABLES T USING (TABLE_NAME) WHERE TABLE_NAME = 'albums' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '' AND (T.PARENT_TABLE_NAME IS NULL OR COLUMN_NAME NOT IN ( SELECT COLUMN_NAME FROM TABLE_PK_COLS WHERE TABLE_NAME = T.PARENT_TABLE_NAME )) ORDER BY ORDINAL_POSITION" + sql = MockServerTests.primary_key_columns_sql "albums", parent_keys: false register_albums_key_columns_result spanner_mock_server, sql, false end def self.register_albums_primary_and_parent_key_columns_result spanner_mock_server - sql = "WITH TABLE_PK_COLS AS ( SELECT C.TABLE_NAME, C.COLUMN_NAME, C.INDEX_NAME, C.COLUMN_ORDERING, C.ORDINAL_POSITION FROM INFORMATION_SCHEMA.INDEX_COLUMNS C WHERE C.INDEX_TYPE = 'PRIMARY_KEY' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '') SELECT INDEX_NAME, COLUMN_NAME, COLUMN_ORDERING, ORDINAL_POSITION FROM TABLE_PK_COLS INNER JOIN INFORMATION_SCHEMA.TABLES T USING (TABLE_NAME) WHERE TABLE_NAME = 'albums' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '' ORDER BY ORDINAL_POSITION" + sql = MockServerTests.primary_key_columns_sql "albums", parent_keys: true register_albums_key_columns_result spanner_mock_server, sql, true end @@ -348,9 +326,9 @@ def self.register_albums_key_columns_result spanner_mock_server, sql, include_pa end def self.register_tracks_columns_result spanner_mock_server - register_commit_timestamps_result spanner_mock_server, "tracks" + MockServerTests.register_commit_timestamps_result spanner_mock_server, "tracks" - sql = "SELECT COLUMN_NAME, SPANNER_TYPE, IS_NULLABLE, GENERATION_EXPRESSION, CAST(COLUMN_DEFAULT AS STRING) AS COLUMN_DEFAULT, ORDINAL_POSITION FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='tracks' ORDER BY ORDINAL_POSITION ASC" + sql = MockServerTests.table_columns_sql "tracks" column_name = Field.new name: "COLUMN_NAME", type: Type.new(code: TypeCode::STRING) spanner_type = Field.new name: "SPANNER_TYPE", type: Type.new(code: TypeCode::STRING) @@ -418,12 +396,12 @@ def self.register_tracks_columns_result spanner_mock_server end def self.register_tracks_primary_key_columns_result spanner_mock_server - sql = "WITH TABLE_PK_COLS AS ( SELECT C.TABLE_NAME, C.COLUMN_NAME, C.INDEX_NAME, C.COLUMN_ORDERING, C.ORDINAL_POSITION FROM INFORMATION_SCHEMA.INDEX_COLUMNS C WHERE C.INDEX_TYPE = 'PRIMARY_KEY' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '') SELECT INDEX_NAME, COLUMN_NAME, COLUMN_ORDERING, ORDINAL_POSITION FROM TABLE_PK_COLS INNER JOIN INFORMATION_SCHEMA.TABLES T USING (TABLE_NAME) WHERE TABLE_NAME = 'tracks' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '' AND (T.PARENT_TABLE_NAME IS NULL OR COLUMN_NAME NOT IN ( SELECT COLUMN_NAME FROM TABLE_PK_COLS WHERE TABLE_NAME = T.PARENT_TABLE_NAME )) ORDER BY ORDINAL_POSITION" + sql = MockServerTests.primary_key_columns_sql "tracks", parent_keys: false register_tracks_key_columns_result spanner_mock_server, sql, false end def self.register_tracks_primary_and_parent_key_columns_result spanner_mock_server - sql = "WITH TABLE_PK_COLS AS ( SELECT C.TABLE_NAME, C.COLUMN_NAME, C.INDEX_NAME, C.COLUMN_ORDERING, C.ORDINAL_POSITION FROM INFORMATION_SCHEMA.INDEX_COLUMNS C WHERE C.INDEX_TYPE = 'PRIMARY_KEY' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '') SELECT INDEX_NAME, COLUMN_NAME, COLUMN_ORDERING, ORDINAL_POSITION FROM TABLE_PK_COLS INNER JOIN INFORMATION_SCHEMA.TABLES T USING (TABLE_NAME) WHERE TABLE_NAME = 'tracks' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '' ORDER BY ORDINAL_POSITION" + sql = MockServerTests.primary_key_columns_sql "tracks", parent_keys: true register_tracks_key_columns_result spanner_mock_server, sql, true end diff --git a/test/activerecord_spanner_interleaved_table_version_7_1_and_higher/model_helper.rb b/test/activerecord_spanner_interleaved_table_version_7_1_and_higher/model_helper.rb index 76f9eab2..ca9795a5 100644 --- a/test/activerecord_spanner_interleaved_table_version_7_1_and_higher/model_helper.rb +++ b/test/activerecord_spanner_interleaved_table_version_7_1_and_higher/model_helper.rb @@ -11,6 +11,7 @@ return if ActiveRecord::gem_version < Gem::Version.create('7.1.0') +require_relative "../activerecord_spanner_mock_server/model_helper" require_relative "models/singer" require_relative "models/album" @@ -151,9 +152,9 @@ def self.register_select_tables_result spanner_mock_server end def self.register_singers_columns_result spanner_mock_server - register_commit_timestamps_result spanner_mock_server, "singers" + MockServerTests.register_commit_timestamps_result spanner_mock_server, "singers" - sql = "SELECT COLUMN_NAME, SPANNER_TYPE, IS_NULLABLE, GENERATION_EXPRESSION, CAST(COLUMN_DEFAULT AS STRING) AS COLUMN_DEFAULT, ORDINAL_POSITION FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='singers' ORDER BY ORDINAL_POSITION ASC" + sql = MockServerTests.table_columns_sql "singers" column_name = Field.new name: "COLUMN_NAME", type: Type.new(code: TypeCode::STRING) spanner_type = Field.new name: "SPANNER_TYPE", type: Type.new(code: TypeCode::STRING) @@ -200,36 +201,13 @@ def self.register_singers_columns_result spanner_mock_server spanner_mock_server.put_statement_result sql, StatementResult.new(result_set) end - def self.register_commit_timestamps_result spanner_mock_server, table_name, column_name = nil, commit_timestamps_col = nil - option_sql = +"SELECT COLUMN_NAME, OPTION_NAME, OPTION_TYPE, OPTION_VALUE FROM INFORMATION_SCHEMA.COLUMN_OPTIONS WHERE TABLE_NAME='#{table_name}'" - option_sql << " AND COLUMN_NAME='#{column_name}'" if column_name - column_name = Field.new name: "COLUMN_NAME", type: Type.new(code: TypeCode::STRING) - option_name = Field.new name: "OPTION_NAME", type: Type.new(code: TypeCode::STRING) - option_type = Field.new name: "OPTION_TYPE", type: Type.new(code: TypeCode::STRING) - option_value = Field.new name: "OPTION_VALUE", type: Type.new(code: TypeCode::STRING) - metadata = ResultSetMetadata.new row_type: StructType.new - metadata.row_type.fields.push column_name, option_name, option_type, option_value - result_set = ResultSet.new metadata: metadata - row = ListValue.new - if commit_timestamps_col - row.values.push( - Value.new(string_value: commit_timestamps_col), - Value.new(string_value: "allow_commit_timestamp"), - Value.new(string_value: "BOOL"), - Value.new(string_value: "TRUE"), - ) - end - result_set.rows.push row - spanner_mock_server.put_statement_result option_sql, StatementResult.new(result_set) - end - def self.register_singers_primary_key_columns_result spanner_mock_server - sql = "WITH TABLE_PK_COLS AS ( SELECT C.TABLE_NAME, C.COLUMN_NAME, C.INDEX_NAME, C.COLUMN_ORDERING, C.ORDINAL_POSITION FROM INFORMATION_SCHEMA.INDEX_COLUMNS C WHERE C.INDEX_TYPE = 'PRIMARY_KEY' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '') SELECT INDEX_NAME, COLUMN_NAME, COLUMN_ORDERING, ORDINAL_POSITION FROM TABLE_PK_COLS INNER JOIN INFORMATION_SCHEMA.TABLES T USING (TABLE_NAME) WHERE TABLE_NAME = 'singers' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '' AND (T.PARENT_TABLE_NAME IS NULL OR COLUMN_NAME NOT IN ( SELECT COLUMN_NAME FROM TABLE_PK_COLS WHERE TABLE_NAME = T.PARENT_TABLE_NAME )) ORDER BY ORDINAL_POSITION" + sql = MockServerTests.primary_key_columns_sql "singers", parent_keys: false register_singers_key_columns_result spanner_mock_server, sql end def self.register_singers_primary_and_parent_key_columns_result spanner_mock_server - sql = "WITH TABLE_PK_COLS AS ( SELECT C.TABLE_NAME, C.COLUMN_NAME, C.INDEX_NAME, C.COLUMN_ORDERING, C.ORDINAL_POSITION FROM INFORMATION_SCHEMA.INDEX_COLUMNS C WHERE C.INDEX_TYPE = 'PRIMARY_KEY' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '') SELECT INDEX_NAME, COLUMN_NAME, COLUMN_ORDERING, ORDINAL_POSITION FROM TABLE_PK_COLS INNER JOIN INFORMATION_SCHEMA.TABLES T USING (TABLE_NAME) WHERE TABLE_NAME = 'singers' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '' ORDER BY ORDINAL_POSITION" + sql = MockServerTests.primary_key_columns_sql "singers", parent_keys: true register_singers_key_columns_result spanner_mock_server, sql end @@ -256,9 +234,9 @@ def self.register_singers_key_columns_result spanner_mock_server, sql end def self.register_albums_columns_result spanner_mock_server - register_commit_timestamps_result spanner_mock_server, "albums" + MockServerTests.register_commit_timestamps_result spanner_mock_server, "albums" - sql = "SELECT COLUMN_NAME, SPANNER_TYPE, IS_NULLABLE, GENERATION_EXPRESSION, CAST(COLUMN_DEFAULT AS STRING) AS COLUMN_DEFAULT, ORDINAL_POSITION FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='albums' ORDER BY ORDINAL_POSITION ASC" + sql = MockServerTests.table_columns_sql "albums" column_name = Field.new name: "COLUMN_NAME", type: Type.new(code: TypeCode::STRING) spanner_type = Field.new name: "SPANNER_TYPE", type: Type.new(code: TypeCode::STRING) @@ -306,12 +284,12 @@ def self.register_albums_columns_result spanner_mock_server end def self.register_albums_primary_key_columns_result spanner_mock_server - sql = "WITH TABLE_PK_COLS AS ( SELECT C.TABLE_NAME, C.COLUMN_NAME, C.INDEX_NAME, C.COLUMN_ORDERING, C.ORDINAL_POSITION FROM INFORMATION_SCHEMA.INDEX_COLUMNS C WHERE C.INDEX_TYPE = 'PRIMARY_KEY' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '') SELECT INDEX_NAME, COLUMN_NAME, COLUMN_ORDERING, ORDINAL_POSITION FROM TABLE_PK_COLS INNER JOIN INFORMATION_SCHEMA.TABLES T USING (TABLE_NAME) WHERE TABLE_NAME = 'albums' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '' AND (T.PARENT_TABLE_NAME IS NULL OR COLUMN_NAME NOT IN ( SELECT COLUMN_NAME FROM TABLE_PK_COLS WHERE TABLE_NAME = T.PARENT_TABLE_NAME )) ORDER BY ORDINAL_POSITION" + sql = MockServerTests.primary_key_columns_sql "albums", parent_keys: false register_albums_key_columns_result spanner_mock_server, sql, false end def self.register_albums_primary_and_parent_key_columns_result spanner_mock_server - sql = "WITH TABLE_PK_COLS AS ( SELECT C.TABLE_NAME, C.COLUMN_NAME, C.INDEX_NAME, C.COLUMN_ORDERING, C.ORDINAL_POSITION FROM INFORMATION_SCHEMA.INDEX_COLUMNS C WHERE C.INDEX_TYPE = 'PRIMARY_KEY' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '') SELECT INDEX_NAME, COLUMN_NAME, COLUMN_ORDERING, ORDINAL_POSITION FROM TABLE_PK_COLS INNER JOIN INFORMATION_SCHEMA.TABLES T USING (TABLE_NAME) WHERE TABLE_NAME = 'albums' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '' ORDER BY ORDINAL_POSITION" + sql = MockServerTests.primary_key_columns_sql "albums", parent_keys: true register_albums_key_columns_result spanner_mock_server, sql, true end @@ -348,9 +326,9 @@ def self.register_albums_key_columns_result spanner_mock_server, sql, include_pa end def self.register_tracks_columns_result spanner_mock_server - register_commit_timestamps_result spanner_mock_server, "tracks" + MockServerTests.register_commit_timestamps_result spanner_mock_server, "tracks" - sql = "SELECT COLUMN_NAME, SPANNER_TYPE, IS_NULLABLE, GENERATION_EXPRESSION, CAST(COLUMN_DEFAULT AS STRING) AS COLUMN_DEFAULT, ORDINAL_POSITION FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='tracks' ORDER BY ORDINAL_POSITION ASC" + sql = MockServerTests.table_columns_sql "tracks" column_name = Field.new name: "COLUMN_NAME", type: Type.new(code: TypeCode::STRING) spanner_type = Field.new name: "SPANNER_TYPE", type: Type.new(code: TypeCode::STRING) @@ -418,12 +396,12 @@ def self.register_tracks_columns_result spanner_mock_server end def self.register_tracks_primary_key_columns_result spanner_mock_server - sql = "WITH TABLE_PK_COLS AS ( SELECT C.TABLE_NAME, C.COLUMN_NAME, C.INDEX_NAME, C.COLUMN_ORDERING, C.ORDINAL_POSITION FROM INFORMATION_SCHEMA.INDEX_COLUMNS C WHERE C.INDEX_TYPE = 'PRIMARY_KEY' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '') SELECT INDEX_NAME, COLUMN_NAME, COLUMN_ORDERING, ORDINAL_POSITION FROM TABLE_PK_COLS INNER JOIN INFORMATION_SCHEMA.TABLES T USING (TABLE_NAME) WHERE TABLE_NAME = 'tracks' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '' AND (T.PARENT_TABLE_NAME IS NULL OR COLUMN_NAME NOT IN ( SELECT COLUMN_NAME FROM TABLE_PK_COLS WHERE TABLE_NAME = T.PARENT_TABLE_NAME )) ORDER BY ORDINAL_POSITION" + sql = MockServerTests.primary_key_columns_sql "tracks", parent_keys: false register_tracks_key_columns_result spanner_mock_server, sql, false end def self.register_tracks_primary_and_parent_key_columns_result spanner_mock_server - sql = "WITH TABLE_PK_COLS AS ( SELECT C.TABLE_NAME, C.COLUMN_NAME, C.INDEX_NAME, C.COLUMN_ORDERING, C.ORDINAL_POSITION FROM INFORMATION_SCHEMA.INDEX_COLUMNS C WHERE C.INDEX_TYPE = 'PRIMARY_KEY' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '') SELECT INDEX_NAME, COLUMN_NAME, COLUMN_ORDERING, ORDINAL_POSITION FROM TABLE_PK_COLS INNER JOIN INFORMATION_SCHEMA.TABLES T USING (TABLE_NAME) WHERE TABLE_NAME = 'tracks' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '' ORDER BY ORDINAL_POSITION" + sql = MockServerTests.primary_key_columns_sql "tracks", parent_keys: true register_tracks_key_columns_result spanner_mock_server, sql, true end diff --git a/test/activerecord_spanner_mock_server/model_helper.rb b/test/activerecord_spanner_mock_server/model_helper.rb index 0773a9c5..bcbd28bb 100644 --- a/test/activerecord_spanner_mock_server/model_helper.rb +++ b/test/activerecord_spanner_mock_server/model_helper.rb @@ -97,6 +97,34 @@ def self.create_random_albums_result(row_count) StatementResult.new result_set end + def self.primary_key_columns_sql table_name, parent_keys: false + sql = +"WITH TABLE_PK_COLS AS ( SELECT C.TABLE_CATALOG, C.TABLE_SCHEMA, C.TABLE_NAME, C.COLUMN_NAME, C.INDEX_NAME, " + sql << "C.COLUMN_ORDERING, C.ORDINAL_POSITION " + sql << "FROM INFORMATION_SCHEMA.INDEX_COLUMNS C " + sql << "WHERE C.INDEX_TYPE = 'PRIMARY_KEY' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '') " + sql << "SELECT INDEX_NAME, COLUMN_NAME, COLUMN_ORDERING, ORDINAL_POSITION " + sql << "FROM TABLE_PK_COLS " + sql << "INNER JOIN INFORMATION_SCHEMA.TABLES T USING (TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME) " + sql << "WHERE TABLE_NAME = '%s' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '' " + unless parent_keys + sql << "AND (T.PARENT_TABLE_NAME IS NULL OR COLUMN_NAME NOT IN ( " + sql << "SELECT COLUMN_NAME FROM TABLE_PK_COLS " + sql << "WHERE TABLE_CATALOG = T.TABLE_CATALOG AND TABLE_SCHEMA=T.TABLE_SCHEMA AND TABLE_NAME = T.PARENT_TABLE_NAME )) " + end + sql << "ORDER BY ORDINAL_POSITION" + sql % { table_name: table_name} + end + + def self.table_columns_sql table_name, column_name: nil + sql = +"SELECT COLUMN_NAME, SPANNER_TYPE, IS_NULLABLE, GENERATION_EXPRESSION, " + sql << "CAST(COLUMN_DEFAULT AS STRING) AS COLUMN_DEFAULT, ORDINAL_POSITION " + sql << "FROM INFORMATION_SCHEMA.COLUMNS " + sql << "WHERE TABLE_NAME='%s' AND TABLE_SCHEMA='' " + sql << "AND COLUMN_NAME='%s' " if column_name + sql << "ORDER BY ORDINAL_POSITION ASC" + sql % { table_name: table_name, column_name: column_name } + end + def self.register_select_tables_result spanner_mock_server sql = "SELECT TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME, PARENT_TABLE_NAME, ON_DELETE_ACTION FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA=''" @@ -179,7 +207,7 @@ def self.register_versioned_singers_columns_result spanner_mock_server def self.register_singers_columns_result_with_options spanner_mock_server, table_name, with_version_column register_commit_timestamps_result spanner_mock_server, table_name - sql = "SELECT COLUMN_NAME, SPANNER_TYPE, IS_NULLABLE, GENERATION_EXPRESSION, CAST(COLUMN_DEFAULT AS STRING) AS COLUMN_DEFAULT, ORDINAL_POSITION FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='#{table_name}' ORDER BY ORDINAL_POSITION ASC" + sql = table_columns_sql table_name column_name = Field.new name: "COLUMN_NAME", type: Type.new(code: TypeCode::STRING) spanner_type = Field.new name: "SPANNER_TYPE", type: Type.new(code: TypeCode::STRING) @@ -279,34 +307,29 @@ def self.register_singers_indexes_result spanner_mock_server end def self.register_singers_primary_key_columns_result spanner_mock_server - sql = "WITH TABLE_PK_COLS AS ( SELECT C.TABLE_NAME, C.COLUMN_NAME, C.INDEX_NAME, C.COLUMN_ORDERING, C.ORDINAL_POSITION FROM INFORMATION_SCHEMA.INDEX_COLUMNS C WHERE C.INDEX_TYPE = 'PRIMARY_KEY' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '') SELECT INDEX_NAME, COLUMN_NAME, COLUMN_ORDERING, ORDINAL_POSITION FROM TABLE_PK_COLS INNER JOIN INFORMATION_SCHEMA.TABLES T USING (TABLE_NAME) WHERE TABLE_NAME = 'singers' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '' AND (T.PARENT_TABLE_NAME IS NULL OR COLUMN_NAME NOT IN ( SELECT COLUMN_NAME FROM TABLE_PK_COLS WHERE TABLE_NAME = T.PARENT_TABLE_NAME )) ORDER BY ORDINAL_POSITION" - register_key_columns_result spanner_mock_server, sql - end - - def self.register_singers_primary_key_columns_result spanner_mock_server - sql = "WITH TABLE_PK_COLS AS ( SELECT C.TABLE_NAME, C.COLUMN_NAME, C.INDEX_NAME, C.COLUMN_ORDERING, C.ORDINAL_POSITION FROM INFORMATION_SCHEMA.INDEX_COLUMNS C WHERE C.INDEX_TYPE = 'PRIMARY_KEY' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '') SELECT INDEX_NAME, COLUMN_NAME, COLUMN_ORDERING, ORDINAL_POSITION FROM TABLE_PK_COLS INNER JOIN INFORMATION_SCHEMA.TABLES T USING (TABLE_NAME) WHERE TABLE_NAME = 'singers' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '' AND (T.PARENT_TABLE_NAME IS NULL OR COLUMN_NAME NOT IN ( SELECT COLUMN_NAME FROM TABLE_PK_COLS WHERE TABLE_NAME = T.PARENT_TABLE_NAME )) ORDER BY ORDINAL_POSITION" + sql = self.primary_key_columns_sql "singers", parent_keys: false register_key_columns_result spanner_mock_server, sql end def self.register_singers_primary_and_parent_key_columns_result spanner_mock_server - sql = "WITH TABLE_PK_COLS AS ( SELECT C.TABLE_NAME, C.COLUMN_NAME, C.INDEX_NAME, C.COLUMN_ORDERING, C.ORDINAL_POSITION FROM INFORMATION_SCHEMA.INDEX_COLUMNS C WHERE C.INDEX_TYPE = 'PRIMARY_KEY' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '') SELECT INDEX_NAME, COLUMN_NAME, COLUMN_ORDERING, ORDINAL_POSITION FROM TABLE_PK_COLS INNER JOIN INFORMATION_SCHEMA.TABLES T USING (TABLE_NAME) WHERE TABLE_NAME = 'singers' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '' ORDER BY ORDINAL_POSITION" + sql = self.primary_key_columns_sql "singers", parent_keys: true register_key_columns_result spanner_mock_server, sql end def self.register_versioned_singers_primary_key_columns_result spanner_mock_server - sql = "WITH TABLE_PK_COLS AS ( SELECT C.TABLE_NAME, C.COLUMN_NAME, C.INDEX_NAME, C.COLUMN_ORDERING, C.ORDINAL_POSITION FROM INFORMATION_SCHEMA.INDEX_COLUMNS C WHERE C.INDEX_TYPE = 'PRIMARY_KEY' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '') SELECT INDEX_NAME, COLUMN_NAME, COLUMN_ORDERING, ORDINAL_POSITION FROM TABLE_PK_COLS INNER JOIN INFORMATION_SCHEMA.TABLES T USING (TABLE_NAME) WHERE TABLE_NAME = 'versioned_singers' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '' AND (T.PARENT_TABLE_NAME IS NULL OR COLUMN_NAME NOT IN ( SELECT COLUMN_NAME FROM TABLE_PK_COLS WHERE TABLE_NAME = T.PARENT_TABLE_NAME )) ORDER BY ORDINAL_POSITION" + sql = primary_key_columns_sql "versioned_singers", parent_keys: false register_key_columns_result spanner_mock_server, sql end def self.register_versioned_singers_primary_and_parent_key_columns_result spanner_mock_server - sql = "WITH TABLE_PK_COLS AS ( SELECT C.TABLE_NAME, C.COLUMN_NAME, C.INDEX_NAME, C.COLUMN_ORDERING, C.ORDINAL_POSITION FROM INFORMATION_SCHEMA.INDEX_COLUMNS C WHERE C.INDEX_TYPE = 'PRIMARY_KEY' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '') SELECT INDEX_NAME, COLUMN_NAME, COLUMN_ORDERING, ORDINAL_POSITION FROM TABLE_PK_COLS INNER JOIN INFORMATION_SCHEMA.TABLES T USING (TABLE_NAME) WHERE TABLE_NAME = 'versioned_singers' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '' ORDER BY ORDINAL_POSITION" + sql = primary_key_columns_sql "versioned_singers", parent_keys: true register_key_columns_result spanner_mock_server, sql end def self.register_albums_columns_result spanner_mock_server register_commit_timestamps_result spanner_mock_server, "albums" - sql = "SELECT COLUMN_NAME, SPANNER_TYPE, IS_NULLABLE, GENERATION_EXPRESSION, CAST(COLUMN_DEFAULT AS STRING) AS COLUMN_DEFAULT, ORDINAL_POSITION FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='albums' ORDER BY ORDINAL_POSITION ASC" + sql = table_columns_sql "albums" column_name = Field.new name: "COLUMN_NAME", type: Type.new(code: TypeCode::STRING) spanner_type = Field.new name: "SPANNER_TYPE", type: Type.new(code: TypeCode::STRING) @@ -354,19 +377,19 @@ def self.register_albums_columns_result spanner_mock_server end def self.register_albums_primary_key_columns_result spanner_mock_server - sql = "WITH TABLE_PK_COLS AS ( SELECT C.TABLE_NAME, C.COLUMN_NAME, C.INDEX_NAME, C.COLUMN_ORDERING, C.ORDINAL_POSITION FROM INFORMATION_SCHEMA.INDEX_COLUMNS C WHERE C.INDEX_TYPE = 'PRIMARY_KEY' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '') SELECT INDEX_NAME, COLUMN_NAME, COLUMN_ORDERING, ORDINAL_POSITION FROM TABLE_PK_COLS INNER JOIN INFORMATION_SCHEMA.TABLES T USING (TABLE_NAME) WHERE TABLE_NAME = 'albums' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '' AND (T.PARENT_TABLE_NAME IS NULL OR COLUMN_NAME NOT IN ( SELECT COLUMN_NAME FROM TABLE_PK_COLS WHERE TABLE_NAME = T.PARENT_TABLE_NAME )) ORDER BY ORDINAL_POSITION" + sql = primary_key_columns_sql "albums", parent_keys: false register_key_columns_result spanner_mock_server, sql end def self.register_albums_primary_and_parent_key_columns_result spanner_mock_server - sql = "WITH TABLE_PK_COLS AS ( SELECT C.TABLE_NAME, C.COLUMN_NAME, C.INDEX_NAME, C.COLUMN_ORDERING, C.ORDINAL_POSITION FROM INFORMATION_SCHEMA.INDEX_COLUMNS C WHERE C.INDEX_TYPE = 'PRIMARY_KEY' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '') SELECT INDEX_NAME, COLUMN_NAME, COLUMN_ORDERING, ORDINAL_POSITION FROM TABLE_PK_COLS INNER JOIN INFORMATION_SCHEMA.TABLES T USING (TABLE_NAME) WHERE TABLE_NAME = 'albums' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '' ORDER BY ORDINAL_POSITION" + sql = primary_key_columns_sql "albums", parent_keys: true register_key_columns_result spanner_mock_server, sql end def self.register_all_types_columns_result spanner_mock_server register_commit_timestamps_result spanner_mock_server, "all_types" - sql = "SELECT COLUMN_NAME, SPANNER_TYPE, IS_NULLABLE, GENERATION_EXPRESSION, CAST(COLUMN_DEFAULT AS STRING) AS COLUMN_DEFAULT, ORDINAL_POSITION FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='all_types' ORDER BY ORDINAL_POSITION ASC" + sql = table_columns_sql "all_types" column_name = Field.new name: "COLUMN_NAME", type: Type.new(code: TypeCode::STRING) spanner_type = Field.new name: "SPANNER_TYPE", type: Type.new(code: TypeCode::STRING) @@ -575,19 +598,19 @@ def self.register_all_types_columns_result spanner_mock_server end def self.register_all_types_primary_key_columns_result spanner_mock_server - sql = "WITH TABLE_PK_COLS AS ( SELECT C.TABLE_NAME, C.COLUMN_NAME, C.INDEX_NAME, C.COLUMN_ORDERING, C.ORDINAL_POSITION FROM INFORMATION_SCHEMA.INDEX_COLUMNS C WHERE C.INDEX_TYPE = 'PRIMARY_KEY' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '') SELECT INDEX_NAME, COLUMN_NAME, COLUMN_ORDERING, ORDINAL_POSITION FROM TABLE_PK_COLS INNER JOIN INFORMATION_SCHEMA.TABLES T USING (TABLE_NAME) WHERE TABLE_NAME = 'all_types' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '' AND (T.PARENT_TABLE_NAME IS NULL OR COLUMN_NAME NOT IN ( SELECT COLUMN_NAME FROM TABLE_PK_COLS WHERE TABLE_NAME = T.PARENT_TABLE_NAME )) ORDER BY ORDINAL_POSITION" + sql = primary_key_columns_sql "all_types", parent_keys: false register_key_columns_result spanner_mock_server, sql end def self.register_all_types_primary_and_parent_key_columns_result spanner_mock_server - sql = "WITH TABLE_PK_COLS AS ( SELECT C.TABLE_NAME, C.COLUMN_NAME, C.INDEX_NAME, C.COLUMN_ORDERING, C.ORDINAL_POSITION FROM INFORMATION_SCHEMA.INDEX_COLUMNS C WHERE C.INDEX_TYPE = 'PRIMARY_KEY' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '') SELECT INDEX_NAME, COLUMN_NAME, COLUMN_ORDERING, ORDINAL_POSITION FROM TABLE_PK_COLS INNER JOIN INFORMATION_SCHEMA.TABLES T USING (TABLE_NAME) WHERE TABLE_NAME = 'all_types' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '' ORDER BY ORDINAL_POSITION" + sql = primary_key_columns_sql "all_types", parent_keys: true register_key_columns_result spanner_mock_server, sql end def self.register_table_with_commit_timestamps_columns_result spanner_mock_server register_commit_timestamps_result spanner_mock_server, "table_with_commit_timestamps", nil, "last_updated" - sql = "SELECT COLUMN_NAME, SPANNER_TYPE, IS_NULLABLE, GENERATION_EXPRESSION, CAST(COLUMN_DEFAULT AS STRING) AS COLUMN_DEFAULT, ORDINAL_POSITION FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='table_with_commit_timestamps' ORDER BY ORDINAL_POSITION ASC" + sql = table_columns_sql "table_with_commit_timestamps" column_name = Field.new name: "COLUMN_NAME", type: Type.new(code: TypeCode::STRING) spanner_type = Field.new name: "SPANNER_TYPE", type: Type.new(code: TypeCode::STRING) @@ -635,19 +658,19 @@ def self.register_table_with_commit_timestamps_columns_result spanner_mock_serve end def self.register_table_with_commit_timestamps_primary_key_columns_result spanner_mock_server - sql = "WITH TABLE_PK_COLS AS ( SELECT C.TABLE_NAME, C.COLUMN_NAME, C.INDEX_NAME, C.COLUMN_ORDERING, C.ORDINAL_POSITION FROM INFORMATION_SCHEMA.INDEX_COLUMNS C WHERE C.INDEX_TYPE = 'PRIMARY_KEY' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '') SELECT INDEX_NAME, COLUMN_NAME, COLUMN_ORDERING, ORDINAL_POSITION FROM TABLE_PK_COLS INNER JOIN INFORMATION_SCHEMA.TABLES T USING (TABLE_NAME) WHERE TABLE_NAME = 'table_with_commit_timestamps' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '' AND (T.PARENT_TABLE_NAME IS NULL OR COLUMN_NAME NOT IN ( SELECT COLUMN_NAME FROM TABLE_PK_COLS WHERE TABLE_NAME = T.PARENT_TABLE_NAME )) ORDER BY ORDINAL_POSITION" + sql = primary_key_columns_sql "table_with_commit_timestamps", parent_keys: false register_key_columns_result spanner_mock_server, sql end def self.register_table_with_commit_timestamps_primary_and_parent_key_columns_result spanner_mock_server - sql = "WITH TABLE_PK_COLS AS ( SELECT C.TABLE_NAME, C.COLUMN_NAME, C.INDEX_NAME, C.COLUMN_ORDERING, C.ORDINAL_POSITION FROM INFORMATION_SCHEMA.INDEX_COLUMNS C WHERE C.INDEX_TYPE = 'PRIMARY_KEY' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '') SELECT INDEX_NAME, COLUMN_NAME, COLUMN_ORDERING, ORDINAL_POSITION FROM TABLE_PK_COLS INNER JOIN INFORMATION_SCHEMA.TABLES T USING (TABLE_NAME) WHERE TABLE_NAME = 'table_with_commit_timestamps' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '' ORDER BY ORDINAL_POSITION" + sql = primary_key_columns_sql "table_with_commit_timestamps", parent_keys: true register_key_columns_result spanner_mock_server, sql end def self.register_table_with_sequence_columns_result spanner_mock_server register_commit_timestamps_result spanner_mock_server, "table_with_sequence" - sql = "SELECT COLUMN_NAME, SPANNER_TYPE, IS_NULLABLE, GENERATION_EXPRESSION, CAST(COLUMN_DEFAULT AS STRING) AS COLUMN_DEFAULT, ORDINAL_POSITION FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='table_with_sequence' ORDER BY ORDINAL_POSITION ASC" + sql = table_columns_sql "table_with_sequence" column_name = Google::Cloud::Spanner::V1::StructType::Field.new name: "COLUMN_NAME", type: Google::Cloud::Spanner::V1::Type.new(code: Google::Cloud::Spanner::V1::TypeCode::STRING) spanner_type = Google::Cloud::Spanner::V1::StructType::Field.new name: "SPANNER_TYPE", type: Google::Cloud::Spanner::V1::Type.new(code: Google::Cloud::Spanner::V1::TypeCode::STRING) @@ -695,12 +718,12 @@ def self.register_table_with_sequence_columns_result spanner_mock_server end def self.register_table_with_sequence_primary_key_columns_result spanner_mock_server - sql = "WITH TABLE_PK_COLS AS ( SELECT C.TABLE_NAME, C.COLUMN_NAME, C.INDEX_NAME, C.COLUMN_ORDERING, C.ORDINAL_POSITION FROM INFORMATION_SCHEMA.INDEX_COLUMNS C WHERE C.INDEX_TYPE = 'PRIMARY_KEY' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '') SELECT INDEX_NAME, COLUMN_NAME, COLUMN_ORDERING, ORDINAL_POSITION FROM TABLE_PK_COLS INNER JOIN INFORMATION_SCHEMA.TABLES T USING (TABLE_NAME) WHERE TABLE_NAME = 'table_with_sequence' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '' AND (T.PARENT_TABLE_NAME IS NULL OR COLUMN_NAME NOT IN ( SELECT COLUMN_NAME FROM TABLE_PK_COLS WHERE TABLE_NAME = T.PARENT_TABLE_NAME )) ORDER BY ORDINAL_POSITION" + sql = primary_key_columns_sql "table_with_sequence", parent_keys: false register_key_columns_result spanner_mock_server, sql end def self.register_table_with_sequence_primary_and_parent_key_columns_result spanner_mock_server - sql = "WITH TABLE_PK_COLS AS ( SELECT C.TABLE_NAME, C.COLUMN_NAME, C.INDEX_NAME, C.COLUMN_ORDERING, C.ORDINAL_POSITION FROM INFORMATION_SCHEMA.INDEX_COLUMNS C WHERE C.INDEX_TYPE = 'PRIMARY_KEY' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '') SELECT INDEX_NAME, COLUMN_NAME, COLUMN_ORDERING, ORDINAL_POSITION FROM TABLE_PK_COLS INNER JOIN INFORMATION_SCHEMA.TABLES T USING (TABLE_NAME) WHERE TABLE_NAME = 'table_with_sequence' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '' ORDER BY ORDINAL_POSITION" + sql = primary_key_columns_sql "table_with_sequence", parent_keys: true register_key_columns_result spanner_mock_server, sql end @@ -722,7 +745,7 @@ def self.register_empty_select_indexes_result spanner_mock_server, sql private def self.register_commit_timestamps_result spanner_mock_server, table_name, column_name = nil, commit_timestamps_col = nil - option_sql = +"SELECT COLUMN_NAME, OPTION_NAME, OPTION_TYPE, OPTION_VALUE FROM INFORMATION_SCHEMA.COLUMN_OPTIONS WHERE TABLE_NAME='#{table_name}'" + option_sql = +"SELECT COLUMN_NAME, OPTION_NAME, OPTION_TYPE, OPTION_VALUE FROM INFORMATION_SCHEMA.COLUMN_OPTIONS WHERE TABLE_NAME='#{table_name}' AND TABLE_SCHEMA=''" option_sql << " AND COLUMN_NAME='#{column_name}'" if column_name column_name = Field.new name: "COLUMN_NAME", type: Type.new(code: TypeCode::STRING) option_name = Field.new name: "OPTION_NAME", type: Type.new(code: TypeCode::STRING) @@ -767,12 +790,12 @@ def self.register_key_columns_result spanner_mock_server, sql end def self.register_join_table_primary_key_result spanner_mock_server - sql = "WITH TABLE_PK_COLS AS ( SELECT C.TABLE_NAME, C.COLUMN_NAME, C.INDEX_NAME, C.COLUMN_ORDERING, C.ORDINAL_POSITION FROM INFORMATION_SCHEMA.INDEX_COLUMNS C WHERE C.INDEX_TYPE = 'PRIMARY_KEY' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '') SELECT INDEX_NAME, COLUMN_NAME, COLUMN_ORDERING, ORDINAL_POSITION FROM TABLE_PK_COLS INNER JOIN INFORMATION_SCHEMA.TABLES T USING (TABLE_NAME) WHERE TABLE_NAME = 'artists_musics' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '' ORDER BY ORDINAL_POSITION" + sql = MockServerTests.primary_key_columns_sql "artists_musics", parent_keys: true register_key_columns_result spanner_mock_server, sql end def self.register_join_table_key_columns_result spanner_mock_server, table, col1, col2 - sql = "WITH TABLE_PK_COLS AS ( SELECT C.TABLE_NAME, C.COLUMN_NAME, C.INDEX_NAME, C.COLUMN_ORDERING, C.ORDINAL_POSITION FROM INFORMATION_SCHEMA.INDEX_COLUMNS C WHERE C.INDEX_TYPE = 'PRIMARY_KEY' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '') SELECT INDEX_NAME, COLUMN_NAME, COLUMN_ORDERING, ORDINAL_POSITION FROM TABLE_PK_COLS INNER JOIN INFORMATION_SCHEMA.TABLES T USING (TABLE_NAME) WHERE TABLE_NAME = '#{table}' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '' AND (T.PARENT_TABLE_NAME IS NULL OR COLUMN_NAME NOT IN ( SELECT COLUMN_NAME FROM TABLE_PK_COLS WHERE TABLE_NAME = T.PARENT_TABLE_NAME )) ORDER BY ORDINAL_POSITION" + sql = primary_key_columns_sql table, parent_keys: false index_name = Field.new name: "INDEX_NAME", type: Type.new(code: TypeCode::STRING) column_name = Field.new name: "COLUMN_NAME", type: Type.new(code: TypeCode::STRING) @@ -807,7 +830,7 @@ def self.register_join_table_key_columns_result spanner_mock_server, table, col1 def self.register_join_table_columns_result spanner_mock_server, table_name, col1, col2 register_commit_timestamps_result spanner_mock_server, table_name - sql = "SELECT COLUMN_NAME, SPANNER_TYPE, IS_NULLABLE, GENERATION_EXPRESSION, CAST(COLUMN_DEFAULT AS STRING) AS COLUMN_DEFAULT, ORDINAL_POSITION FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='#{table_name}' ORDER BY ORDINAL_POSITION ASC" + sql = table_columns_sql table_name column_name = Field.new name: "COLUMN_NAME", type: Type.new(code: TypeCode::STRING) spanner_type = Field.new name: "SPANNER_TYPE", type: Type.new(code: TypeCode::STRING) diff --git a/test/migrations_with_mock_server/migrations_with_mock_server_test.rb b/test/migrations_with_mock_server/migrations_with_mock_server_test.rb index fe4b75f5..3e02ff3d 100644 --- a/test/migrations_with_mock_server/migrations_with_mock_server_test.rb +++ b/test/migrations_with_mock_server/migrations_with_mock_server_test.rb @@ -15,6 +15,15 @@ require_relative "models/track" module TestMigrationsWithMockServer + Value = Google::Protobuf::Value + ListValue = Google::Protobuf::ListValue + Field = Google::Cloud::Spanner::V1::StructType::Field + Type = Google::Cloud::Spanner::V1::Type + TypeCode = Google::Cloud::Spanner::V1::TypeCode + ResultSetMetadata = Google::Cloud::Spanner::V1::ResultSetMetadata + StructType = Google::Cloud::Spanner::V1::StructType + ResultSet = Google::Cloud::Spanner::V1::ResultSet + # Tests executing a simple migration on a mock Spanner server. class SpannerMigrationsMockServerTest < Minitest::Test VERSION_6_1_0 = Gem::Version.create('6.1.0') @@ -340,17 +349,7 @@ def test_drop_column register_empty_select_index_columns_result select_index_columns_sql select_indexes_sql = "SELECT INDEX_NAME, INDEX_TYPE, IS_UNIQUE, IS_NULL_FILTERED, PARENT_TABLE_NAME, INDEX_STATE FROM INFORMATION_SCHEMA.INDEXES WHERE TABLE_NAME='singers' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '' AND SPANNER_IS_MANAGED=FALSE" MockServerTests::register_empty_select_indexes_result @mock, select_indexes_sql - select_fk_sql = "SELECT cc.table_name AS to_table,\n" - select_fk_sql << " cc.column_name AS primary_key,\n" - select_fk_sql << " fk.column_name as column,\n" - select_fk_sql << " fk.constraint_name AS name,\n" - select_fk_sql << " rc.update_rule AS on_update,\n" - select_fk_sql << " rc.delete_rule AS on_delete\n" - select_fk_sql << "FROM information_schema.referential_constraints rc\n" - select_fk_sql << "INNER JOIN information_schema.key_column_usage fk ON rc.constraint_name = fk.constraint_name\n" - select_fk_sql << "INNER JOIN information_schema.constraint_column_usage cc ON rc.constraint_name = cc.constraint_name\n" - select_fk_sql << "WHERE fk.table_name = 'singers'\n" - select_fk_sql << " AND fk.constraint_schema = ''\n" + select_fk_sql = foreign_keys_sql "singers" register_empty_select_foreign_key_result select_fk_sql with_change_table :singers do |t| @@ -366,7 +365,7 @@ def test_drop_column def test_change_column MockServerTests::register_singers_primary_key_columns_result @mock unless is_7_1_or_higher? MockServerTests::register_singers_primary_and_parent_key_columns_result @mock if is_7_1_or_higher? - select_column_sql = "SELECT COLUMN_NAME, SPANNER_TYPE, IS_NULLABLE, GENERATION_EXPRESSION, CAST(COLUMN_DEFAULT AS STRING) AS COLUMN_DEFAULT, ORDINAL_POSITION FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='singers' AND COLUMN_NAME='age' ORDER BY ORDINAL_POSITION ASC" + select_column_sql = MockServerTests.table_columns_sql "singers", column_name: "age" register_select_single_column_result select_column_sql, "age", "INT64" select_index_columns_sql = "SELECT INDEX_NAME, COLUMN_NAME, COLUMN_ORDERING, ORDINAL_POSITION FROM INFORMATION_SCHEMA.INDEX_COLUMNS WHERE TABLE_NAME='singers' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '' ORDER BY ORDINAL_POSITION ASC" register_empty_select_index_columns_result select_index_columns_sql @@ -387,7 +386,7 @@ def test_change_column_add_not_null MockServerTests::register_singers_primary_key_columns_result @mock unless is_7_1_or_higher? MockServerTests::register_singers_primary_and_parent_key_columns_result @mock if is_7_1_or_higher? - select_column_sql = "SELECT COLUMN_NAME, SPANNER_TYPE, IS_NULLABLE, GENERATION_EXPRESSION, CAST(COLUMN_DEFAULT AS STRING) AS COLUMN_DEFAULT, ORDINAL_POSITION FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='singers' AND COLUMN_NAME='age' ORDER BY ORDINAL_POSITION ASC" + select_column_sql = MockServerTests.table_columns_sql "singers", column_name: "age" register_select_single_column_result select_column_sql, "age", "INT64" select_index_columns_sql = "SELECT INDEX_NAME, COLUMN_NAME, COLUMN_ORDERING, ORDINAL_POSITION FROM INFORMATION_SCHEMA.INDEX_COLUMNS WHERE TABLE_NAME='singers' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '' ORDER BY ORDINAL_POSITION ASC" register_empty_select_index_columns_result select_index_columns_sql @@ -407,7 +406,7 @@ def test_change_column_add_not_null def test_change_column_remove_not_null MockServerTests::register_singers_primary_key_columns_result @mock unless is_7_1_or_higher? MockServerTests::register_singers_primary_and_parent_key_columns_result @mock if is_7_1_or_higher? - select_column_sql = "SELECT COLUMN_NAME, SPANNER_TYPE, IS_NULLABLE, GENERATION_EXPRESSION, CAST(COLUMN_DEFAULT AS STRING) AS COLUMN_DEFAULT, ORDINAL_POSITION FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='singers' AND COLUMN_NAME='age' ORDER BY ORDINAL_POSITION ASC" + select_column_sql = MockServerTests.table_columns_sql "singers", column_name: "age" register_select_single_column_result select_column_sql, "age", "INT64" select_index_columns_sql = "SELECT INDEX_NAME, COLUMN_NAME, COLUMN_ORDERING, ORDINAL_POSITION FROM INFORMATION_SCHEMA.INDEX_COLUMNS WHERE TABLE_NAME='singers' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '' ORDER BY ORDINAL_POSITION ASC" register_empty_select_index_columns_result select_index_columns_sql @@ -429,23 +428,13 @@ def test_rename_column MockServerTests::register_singers_primary_and_parent_key_columns_result @mock if is_7_1_or_higher? # Cloud Spanner does not support renaming a column, so instead the migration will create a new column, copy the # data from the old column to the new column, and then drop the old column. - select_column_sql = "SELECT COLUMN_NAME, SPANNER_TYPE, IS_NULLABLE, GENERATION_EXPRESSION, CAST(COLUMN_DEFAULT AS STRING) AS COLUMN_DEFAULT, ORDINAL_POSITION FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='singers' AND COLUMN_NAME='age' ORDER BY ORDINAL_POSITION ASC" + select_column_sql = MockServerTests.table_columns_sql "singers", column_name: "age" register_select_single_column_result select_column_sql, "age", "INT64" select_index_columns_sql = "SELECT INDEX_NAME, COLUMN_NAME, COLUMN_ORDERING, ORDINAL_POSITION FROM INFORMATION_SCHEMA.INDEX_COLUMNS WHERE TABLE_NAME='singers' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '' ORDER BY ORDINAL_POSITION ASC" register_empty_select_index_columns_result select_index_columns_sql select_indexes_sql = "SELECT INDEX_NAME, INDEX_TYPE, IS_UNIQUE, IS_NULL_FILTERED, PARENT_TABLE_NAME, INDEX_STATE FROM INFORMATION_SCHEMA.INDEXES WHERE TABLE_NAME='singers' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '' AND SPANNER_IS_MANAGED=FALSE" MockServerTests::register_empty_select_indexes_result @mock, select_indexes_sql - select_fk_sql = "SELECT cc.table_name AS to_table,\n" - select_fk_sql << " cc.column_name AS primary_key,\n" - select_fk_sql << " fk.column_name as column,\n" - select_fk_sql << " fk.constraint_name AS name,\n" - select_fk_sql << " rc.update_rule AS on_update,\n" - select_fk_sql << " rc.delete_rule AS on_delete\n" - select_fk_sql << "FROM information_schema.referential_constraints rc\n" - select_fk_sql << "INNER JOIN information_schema.key_column_usage fk ON rc.constraint_name = fk.constraint_name\n" - select_fk_sql << "INNER JOIN information_schema.constraint_column_usage cc ON rc.constraint_name = cc.constraint_name\n" - select_fk_sql << "WHERE fk.table_name = 'singers'\n" - select_fk_sql << " AND fk.constraint_schema = ''\n" + select_fk_sql = foreign_keys_sql "singers" register_empty_select_foreign_key_result select_fk_sql update_data_sql = "UPDATE singers SET `age_at_insert` = `age` WHERE true" @mock.put_statement_result update_data_sql, StatementResult.new(100) @@ -594,17 +583,7 @@ def test_remove_references_column_removes_index_and_column register_single_select_index_columns_result select_index_column_sql, "index_albums_on_singer_id", "singer_id" select_all_index_columns_sql = "SELECT INDEX_NAME, COLUMN_NAME, COLUMN_ORDERING, ORDINAL_POSITION FROM INFORMATION_SCHEMA.INDEX_COLUMNS WHERE TABLE_NAME='albums' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '' ORDER BY ORDINAL_POSITION ASC" register_single_select_index_columns_result select_all_index_columns_sql, "index_albums_on_singer_id", "singer_id" - select_fk_sql = "SELECT cc.table_name AS to_table,\n" - select_fk_sql << " cc.column_name AS primary_key,\n" - select_fk_sql << " fk.column_name as column,\n" - select_fk_sql << " fk.constraint_name AS name,\n" - select_fk_sql << " rc.update_rule AS on_update,\n" - select_fk_sql << " rc.delete_rule AS on_delete\n" - select_fk_sql << "FROM information_schema.referential_constraints rc\n" - select_fk_sql << "INNER JOIN information_schema.key_column_usage fk ON rc.constraint_name = fk.constraint_name\n" - select_fk_sql << "INNER JOIN information_schema.constraint_column_usage cc ON rc.constraint_name = cc.constraint_name\n" - select_fk_sql << "WHERE fk.table_name = 'albums'\n" - select_fk_sql << " AND fk.constraint_schema = ''\n" + select_fk_sql = foreign_keys_sql "albums" register_empty_select_foreign_key_result select_fk_sql [:remove_references, :remove_belongs_to].each do |method| @@ -635,18 +614,8 @@ def test_remove_references_column_removes_foreign_key_and_column register_empty_select_index_columns_result select_index_column_sql select_all_index_columns_sql = "SELECT INDEX_NAME, COLUMN_NAME, COLUMN_ORDERING, ORDINAL_POSITION FROM INFORMATION_SCHEMA.INDEX_COLUMNS WHERE TABLE_NAME='albums' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '' ORDER BY ORDINAL_POSITION ASC" register_empty_select_index_columns_result select_all_index_columns_sql - select_fk_sql = "SELECT cc.table_name AS to_table,\n" - select_fk_sql << " cc.column_name AS primary_key,\n" - select_fk_sql << " fk.column_name as column,\n" - select_fk_sql << " fk.constraint_name AS name,\n" - select_fk_sql << " rc.update_rule AS on_update,\n" - select_fk_sql << " rc.delete_rule AS on_delete\n" - select_fk_sql << "FROM information_schema.referential_constraints rc\n" - select_fk_sql << "INNER JOIN information_schema.key_column_usage fk ON rc.constraint_name = fk.constraint_name\n" - select_fk_sql << "INNER JOIN information_schema.constraint_column_usage cc ON rc.constraint_name = cc.constraint_name\n" - select_fk_sql << "WHERE fk.table_name = 'albums'\n" - select_fk_sql << " AND fk.constraint_schema = ''\n" - register_single_select_foreign_key_result select_fk_sql, "singers", "singer_id", "singer_id", "fk_albums_singer" + select_fk_sql = foreign_keys_sql "albums" + register_single_select_foreign_key_result select_fk_sql, "albums", "singers", "singer_id", "singer_id", "fk_albums_singer" [:remove_references, :remove_belongs_to].each do |method| ActiveRecord::Base.connection.ddl_batch do @@ -894,17 +863,7 @@ def test_remove_drops_single_column MockServerTests::register_empty_select_indexes_result @mock, select_index_sql select_index_columns_sql = "SELECT INDEX_NAME, COLUMN_NAME, COLUMN_ORDERING, ORDINAL_POSITION FROM INFORMATION_SCHEMA.INDEX_COLUMNS WHERE TABLE_NAME='singers' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '' ORDER BY ORDINAL_POSITION ASC" register_empty_select_index_columns_result select_index_columns_sql - select_fk_sql = "SELECT cc.table_name AS to_table,\n" - select_fk_sql << " cc.column_name AS primary_key,\n" - select_fk_sql << " fk.column_name as column,\n" - select_fk_sql << " fk.constraint_name AS name,\n" - select_fk_sql << " rc.update_rule AS on_update,\n" - select_fk_sql << " rc.delete_rule AS on_delete\n" - select_fk_sql << "FROM information_schema.referential_constraints rc\n" - select_fk_sql << "INNER JOIN information_schema.key_column_usage fk ON rc.constraint_name = fk.constraint_name\n" - select_fk_sql << "INNER JOIN information_schema.constraint_column_usage cc ON rc.constraint_name = cc.constraint_name\n" - select_fk_sql << "WHERE fk.table_name = 'singers'\n" - select_fk_sql << " AND fk.constraint_schema = ''\n" + select_fk_sql = foreign_keys_sql "singers" register_empty_select_foreign_key_result select_fk_sql with_change_table :singers do |t| @@ -921,17 +880,7 @@ def test_remove_drops_multiple_columns MockServerTests::register_empty_select_indexes_result @mock, select_index_sql select_index_columns_sql = "SELECT INDEX_NAME, COLUMN_NAME, COLUMN_ORDERING, ORDINAL_POSITION FROM INFORMATION_SCHEMA.INDEX_COLUMNS WHERE TABLE_NAME='singers' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '' ORDER BY ORDINAL_POSITION ASC" register_empty_select_index_columns_result select_index_columns_sql - select_fk_sql = "SELECT cc.table_name AS to_table,\n" - select_fk_sql << " cc.column_name AS primary_key,\n" - select_fk_sql << " fk.column_name as column,\n" - select_fk_sql << " fk.constraint_name AS name,\n" - select_fk_sql << " rc.update_rule AS on_update,\n" - select_fk_sql << " rc.delete_rule AS on_delete\n" - select_fk_sql << "FROM information_schema.referential_constraints rc\n" - select_fk_sql << "INNER JOIN information_schema.key_column_usage fk ON rc.constraint_name = fk.constraint_name\n" - select_fk_sql << "INNER JOIN information_schema.constraint_column_usage cc ON rc.constraint_name = cc.constraint_name\n" - select_fk_sql << "WHERE fk.table_name = 'singers'\n" - select_fk_sql << " AND fk.constraint_schema = ''\n" + select_fk_sql = foreign_keys_sql "singers" register_empty_select_foreign_key_result select_fk_sql with_change_table :singers do |t| t.remove :age, :full_name @@ -946,7 +895,7 @@ def test_remove_drops_multiple_columns def test_change_changes_column MockServerTests::register_singers_primary_key_columns_result @mock unless is_7_1_or_higher? MockServerTests::register_singers_primary_and_parent_key_columns_result @mock if is_7_1_or_higher? - select_column_sql = "SELECT COLUMN_NAME, SPANNER_TYPE, IS_NULLABLE, GENERATION_EXPRESSION, CAST(COLUMN_DEFAULT AS STRING) AS COLUMN_DEFAULT, ORDINAL_POSITION FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='singers' AND COLUMN_NAME='picture' ORDER BY ORDINAL_POSITION ASC" + select_column_sql = MockServerTests.table_columns_sql "singers", column_name: "picture" register_single_select_columns_result select_column_sql, "picture", "BYTES(MAX)" select_index_sql = "SELECT INDEX_NAME, INDEX_TYPE, IS_UNIQUE, IS_NULL_FILTERED, PARENT_TABLE_NAME, INDEX_STATE FROM INFORMATION_SCHEMA.INDEXES WHERE TABLE_NAME='singers' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '' AND SPANNER_IS_MANAGED=FALSE" MockServerTests::register_empty_select_indexes_result @mock, select_index_sql @@ -965,7 +914,7 @@ def test_change_changes_column def test_change_changes_column_with_options MockServerTests::register_singers_primary_key_columns_result @mock unless is_7_1_or_higher? MockServerTests::register_singers_primary_and_parent_key_columns_result @mock if is_7_1_or_higher? - select_column_sql = "SELECT COLUMN_NAME, SPANNER_TYPE, IS_NULLABLE, GENERATION_EXPRESSION, CAST(COLUMN_DEFAULT AS STRING) AS COLUMN_DEFAULT, ORDINAL_POSITION FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='singers' AND COLUMN_NAME='picture' ORDER BY ORDINAL_POSITION ASC" + select_column_sql = MockServerTests.table_columns_sql "singers", column_name: "picture" register_single_select_columns_result select_column_sql, "picture", "BYTES(MAX)" select_index_sql = "SELECT INDEX_NAME, INDEX_TYPE, IS_UNIQUE, IS_NULL_FILTERED, PARENT_TABLE_NAME, INDEX_STATE FROM INFORMATION_SCHEMA.INDEXES WHERE TABLE_NAME='singers' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '' AND SPANNER_IS_MANAGED=FALSE" MockServerTests::register_empty_select_indexes_result @mock, select_index_sql @@ -1079,7 +1028,7 @@ def register_schema_migrations_columns_result # CREATE TABLE `schema_migrations` (`version` STRING(MAX) NOT NULL) PRIMARY KEY (`version`) MockServerTests::register_commit_timestamps_result @mock, "schema_migrations" - sql = "SELECT COLUMN_NAME, SPANNER_TYPE, IS_NULLABLE, GENERATION_EXPRESSION, CAST(COLUMN_DEFAULT AS STRING) AS COLUMN_DEFAULT, ORDINAL_POSITION FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='schema_migrations' ORDER BY ORDINAL_POSITION ASC" + sql = MockServerTests.table_columns_sql "schema_migrations" column_name = Google::Cloud::Spanner::V1::StructType::Field.new name: "COLUMN_NAME", type: Google::Cloud::Spanner::V1::Type.new(code: Google::Cloud::Spanner::V1::TypeCode::STRING) spanner_type = Google::Cloud::Spanner::V1::StructType::Field.new name: "SPANNER_TYPE", type: Google::Cloud::Spanner::V1::Type.new(code: Google::Cloud::Spanner::V1::TypeCode::STRING) @@ -1107,7 +1056,7 @@ def register_schema_migrations_columns_result end def register_schema_migrations_primary_key_result - sql = "WITH TABLE_PK_COLS AS ( SELECT C.TABLE_NAME, C.COLUMN_NAME, C.INDEX_NAME, C.COLUMN_ORDERING, C.ORDINAL_POSITION FROM INFORMATION_SCHEMA.INDEX_COLUMNS C WHERE C.INDEX_TYPE = 'PRIMARY_KEY' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '') SELECT INDEX_NAME, COLUMN_NAME, COLUMN_ORDERING, ORDINAL_POSITION FROM TABLE_PK_COLS INNER JOIN INFORMATION_SCHEMA.TABLES T USING (TABLE_NAME) WHERE TABLE_NAME = 'schema_migrations' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '' AND (T.PARENT_TABLE_NAME IS NULL OR COLUMN_NAME NOT IN ( SELECT COLUMN_NAME FROM TABLE_PK_COLS WHERE TABLE_NAME = T.PARENT_TABLE_NAME )) ORDER BY ORDINAL_POSITION" + sql = MockServerTests.primary_key_columns_sql "schema_migrations", parent_keys: false index_name = Google::Cloud::Spanner::V1::StructType::Field.new name: "INDEX_NAME", type: Google::Cloud::Spanner::V1::Type.new(code: Google::Cloud::Spanner::V1::TypeCode::STRING) column_name = Google::Cloud::Spanner::V1::StructType::Field.new name: "COLUMN_NAME", type: Google::Cloud::Spanner::V1::Type.new(code: Google::Cloud::Spanner::V1::TypeCode::STRING) @@ -1139,7 +1088,7 @@ def register_ar_internal_metadata_columns_result # CREATE TABLE `ar_internal_metadata` (`key` STRING(MAX) NOT NULL, `value` STRING(MAX), `created_at` TIMESTAMP NOT NULL, `updated_at` TIMESTAMP NOT NULL) PRIMARY KEY (`key`) MockServerTests::register_commit_timestamps_result @mock, "ar_internal_metadata" - sql = "SELECT COLUMN_NAME, SPANNER_TYPE, IS_NULLABLE, GENERATION_EXPRESSION, CAST(COLUMN_DEFAULT AS STRING) AS COLUMN_DEFAULT, ORDINAL_POSITION FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='ar_internal_metadata' ORDER BY ORDINAL_POSITION ASC" + sql = MockServerTests.table_columns_sql "ar_internal_metadata" column_name = Google::Cloud::Spanner::V1::StructType::Field.new name: "COLUMN_NAME", type: Google::Cloud::Spanner::V1::Type.new(code: Google::Cloud::Spanner::V1::TypeCode::STRING) spanner_type = Google::Cloud::Spanner::V1::StructType::Field.new name: "SPANNER_TYPE", type: Google::Cloud::Spanner::V1::Type.new(code: Google::Cloud::Spanner::V1::TypeCode::STRING) @@ -1197,7 +1146,7 @@ def register_ar_internal_metadata_columns_result end def register_ar_internal_metadata_primary_key_result - sql = "WITH TABLE_PK_COLS AS ( SELECT C.TABLE_NAME, C.COLUMN_NAME, C.INDEX_NAME, C.COLUMN_ORDERING, C.ORDINAL_POSITION FROM INFORMATION_SCHEMA.INDEX_COLUMNS C WHERE C.INDEX_TYPE = 'PRIMARY_KEY' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '') SELECT INDEX_NAME, COLUMN_NAME, COLUMN_ORDERING, ORDINAL_POSITION FROM TABLE_PK_COLS INNER JOIN INFORMATION_SCHEMA.TABLES T USING (TABLE_NAME) WHERE TABLE_NAME = 'ar_internal_metadata' AND TABLE_CATALOG = '' AND TABLE_SCHEMA = '' AND (T.PARENT_TABLE_NAME IS NULL OR COLUMN_NAME NOT IN ( SELECT COLUMN_NAME FROM TABLE_PK_COLS WHERE TABLE_NAME = T.PARENT_TABLE_NAME )) ORDER BY ORDINAL_POSITION" + sql = MockServerTests.primary_key_columns_sql "ar_internal_metadata", parent_keys: false index_name = Google::Cloud::Spanner::V1::StructType::Field.new name: "INDEX_NAME", type: Google::Cloud::Spanner::V1::Type.new(code: Google::Cloud::Spanner::V1::TypeCode::STRING) column_name = Google::Cloud::Spanner::V1::StructType::Field.new name: "COLUMN_NAME", type: Google::Cloud::Spanner::V1::Type.new(code: Google::Cloud::Spanner::V1::TypeCode::STRING) @@ -1419,25 +1368,53 @@ def register_empty_select_foreign_key_result sql @mock.put_statement_result sql, StatementResult.new(result_set) end - def register_single_select_foreign_key_result sql, to_table, pk_column_name, fk_column_name, constraint_name - col_to_table = Google::Cloud::Spanner::V1::StructType::Field.new name: "to_table", type: Google::Cloud::Spanner::V1::Type.new(code: Google::Cloud::Spanner::V1::TypeCode::STRING) - col_primary_key = Google::Cloud::Spanner::V1::StructType::Field.new name: "primary_key", type: Google::Cloud::Spanner::V1::Type.new(code: Google::Cloud::Spanner::V1::TypeCode::STRING) - col_column = Google::Cloud::Spanner::V1::StructType::Field.new name: "column", type: Google::Cloud::Spanner::V1::Type.new(code: Google::Cloud::Spanner::V1::TypeCode::STRING) - col_name = Google::Cloud::Spanner::V1::StructType::Field.new name: "name", type: Google::Cloud::Spanner::V1::Type.new(code: Google::Cloud::Spanner::V1::TypeCode::STRING) - col_on_update = Google::Cloud::Spanner::V1::StructType::Field.new name: "on_update", type: Google::Cloud::Spanner::V1::Type.new(code: Google::Cloud::Spanner::V1::TypeCode::STRING) - col_on_delete = Google::Cloud::Spanner::V1::StructType::Field.new name: "on_delete", type: Google::Cloud::Spanner::V1::Type.new(code: Google::Cloud::Spanner::V1::TypeCode::STRING) - - metadata = Google::Cloud::Spanner::V1::ResultSetMetadata.new row_type: Google::Cloud::Spanner::V1::StructType.new - metadata.row_type.fields.push col_to_table, col_primary_key, col_column, col_name, col_on_update, col_on_delete - result_set = Google::Cloud::Spanner::V1::ResultSet.new metadata: metadata - row = Google::Protobuf::ListValue.new + def register_single_select_foreign_key_result sql, from_table, to_table, pk_column_name, fk_column_name, constraint_name + col_constraint_catalog = Field.new name: "CONSTRAINT_CATALOG", type: Type.new(code: TypeCode::STRING) + col_constraint_schema = Field.new name: "CONSTRAINT_SCHEMA", type: Type.new(code: TypeCode::STRING) + col_constraint_name = Field.new name: "CONSTRAINT_NAME", type: Type.new(code: TypeCode::STRING) + col_update_rule = Field.new name: "UPDATE_RULE", type: Type.new(code: TypeCode::STRING) + col_delete_rule = Field.new name: "DELETE_RULE", type: Type.new(code: TypeCode::STRING) + col_fk_catalog = Field.new name: "FK_CATALOG", type: Type.new(code: TypeCode::STRING) + col_fk_schema = Field.new name: "FK_SCHEMA", type: Type.new(code: TypeCode::STRING) + col_fk_table = Field.new name: "FK_TABLE", type: Type.new(code: TypeCode::STRING) + col_pk_catalog = Field.new name: "PK_CATALOG", type: Type.new(code: TypeCode::STRING) + col_pk_schema = Field.new name: "PK_SCHEMA", type: Type.new(code: TypeCode::STRING) + col_pk_table = Field.new name: "PK_TABLE", type: Type.new(code: TypeCode::STRING) + col_fk_columns = Field.new name: "FK_COLUMNS", + type: Type.new(code: TypeCode::ARRAY, + array_element_type: Type.new(code: TypeCode::STRING)) + col_pk_columns = Field.new name: "PK_COLUMNS", + type: Type.new(code: TypeCode::ARRAY, + array_element_type: Type.new(code: TypeCode::STRING)) + + metadata = ResultSetMetadata.new row_type: StructType.new + metadata.row_type.fields.push col_constraint_catalog, col_constraint_schema, col_constraint_name, + col_update_rule, col_delete_rule, + col_fk_catalog, col_fk_schema, col_fk_table, + col_pk_catalog, col_pk_schema, col_pk_table, + col_fk_columns, col_pk_columns + result_set = ResultSet.new metadata: metadata + + pk_column_names = ListValue.new + pk_column_names.values.push(Value.new(string_value: pk_column_name)) + fk_column_names = ListValue.new + fk_column_names.values.push(Value.new(string_value: fk_column_name)) + + row = ListValue.new row.values.push( - Google::Protobuf::Value.new(string_value: to_table), - Google::Protobuf::Value.new(string_value: pk_column_name), - Google::Protobuf::Value.new(string_value: fk_column_name), - Google::Protobuf::Value.new(string_value: constraint_name), - Google::Protobuf::Value.new(string_value: "NO_ACTION"), - Google::Protobuf::Value.new(string_value: "NO_ACTION") + Value.new(string_value: ""), # constraint_catalog + Value.new(string_value: ""), # constraint_schema + Value.new(string_value: constraint_name), # constraint_name + Value.new(string_value: "NO_ACTION"), + Value.new(string_value: "NO_ACTION"), + Value.new(string_value: ""), # fk_catalog + Value.new(string_value: ""), # fk_schema + Value.new(string_value: from_table), # fk_table + Value.new(string_value: ""), # pk_catalog + Value.new(string_value: ""), # pk_schema + Value.new(string_value: to_table), # pk_table + Value.new(list_value: fk_column_names), + Value.new(list_value: fk_column_names), ) result_set.rows.push row @@ -1467,5 +1444,45 @@ def register_version_result(from_version, to_version) : "INSERT INTO `schema_migrations` (`version`) VALUES ('%" @mock.put_statement_result update_sql, StatementResult.new(1) end + + def foreign_keys_sql table_name + sql = <<~SQL + SELECT CONSTRAINT_CATALOG, CONSTRAINT_SCHEMA, CONSTRAINT_NAME, UPDATE_RULE, DELETE_RULE, + FK_CATALOG, FK_SCHEMA, FK_TABLE, + PK_CATALOG, PK_SCHEMA, PK_TABLE, + ARRAY_AGG(FK_COLUMN) AS FK_COLUMNS, ARRAY_AGG(PK_COLUMN) AS PK_COLUMNS + FROM (SELECT CONSTRAINTS.CONSTRAINT_CATALOG, + CONSTRAINTS.CONSTRAINT_SCHEMA, + CONSTRAINTS.CONSTRAINT_NAME, + CONSTRAINTS.UPDATE_RULE, + CONSTRAINTS.DELETE_RULE, + CHILD.TABLE_CATALOG AS FK_CATALOG, + CHILD.TABLE_SCHEMA AS FK_SCHEMA, + CHILD.TABLE_NAME AS FK_TABLE, + CHILD.COLUMN_NAME AS FK_COLUMN, + PARENT.TABLE_CATALOG AS PK_CATALOG, + PARENT.TABLE_SCHEMA AS PK_SCHEMA, + PARENT.TABLE_NAME AS PK_TABLE, + PARENT.COLUMN_NAME AS PK_COLUMN + FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS CONSTRAINTS + INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE CHILD + ON CONSTRAINTS.CONSTRAINT_CATALOG = CHILD.CONSTRAINT_CATALOG + AND CONSTRAINTS.CONSTRAINT_SCHEMA = CHILD.CONSTRAINT_SCHEMA + AND CONSTRAINTS.CONSTRAINT_NAME = CHILD.CONSTRAINT_NAME + INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE PARENT + ON CONSTRAINTS.UNIQUE_CONSTRAINT_CATALOG = PARENT.CONSTRAINT_CATALOG + AND CONSTRAINTS.UNIQUE_CONSTRAINT_SCHEMA = PARENT.CONSTRAINT_SCHEMA + AND CONSTRAINTS.UNIQUE_CONSTRAINT_NAME = PARENT.CONSTRAINT_NAME + AND PARENT.ORDINAL_POSITION = CHILD.POSITION_IN_UNIQUE_CONSTRAINT + ORDER BY CHILD.TABLE_CATALOG, CHILD.TABLE_SCHEMA, CHILD.TABLE_NAME, CHILD.POSITION_IN_UNIQUE_CONSTRAINT + ) FOREIGN_KEYS + WHERE FK_TABLE = '%s' + AND FK_SCHEMA = '' + GROUP BY CONSTRAINT_CATALOG, CONSTRAINT_SCHEMA, CONSTRAINT_NAME, UPDATE_RULE, DELETE_RULE, + FK_CATALOG, FK_SCHEMA, FK_TABLE, + PK_CATALOG, PK_SCHEMA, PK_TABLE + SQL + sql % { table_name: table_name } + end end end