diff --git a/CHANGELOG b/CHANGELOG index 89bbddcc5..79a755d6f 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,7 @@ === master +* Support schema-qualified table names when using create_table :temp with a Sequel::SQL::QualifiedIdentifier (jeremyevans) (#2185) + * Support MERGE WHEN NOT MATCHED BY SOURCE on PostgreSQL 17+ (jeremyevans) * Add stdio_logger extension for a minimal logger that Sequel::Database can use (jeremyevans) diff --git a/lib/sequel/adapters/jdbc/derby.rb b/lib/sequel/adapters/jdbc/derby.rb index b97edb7c3..dffdd2080 100644 --- a/lib/sequel/adapters/jdbc/derby.rb +++ b/lib/sequel/adapters/jdbc/derby.rb @@ -115,7 +115,7 @@ def create_table_as_sql(name, sql, options) # Temporary table creation on Derby uses DECLARE instead of CREATE. def create_table_prefix_sql(name, options) if options[:temp] - "DECLARE GLOBAL TEMPORARY TABLE #{quote_identifier(name)}" + "DECLARE GLOBAL TEMPORARY TABLE #{create_table_table_name_sql(name, options)}" else super end diff --git a/lib/sequel/adapters/shared/db2.rb b/lib/sequel/adapters/shared/db2.rb index 83cd6a442..235f853f2 100644 --- a/lib/sequel/adapters/shared/db2.rb +++ b/lib/sequel/adapters/shared/db2.rb @@ -198,7 +198,7 @@ def create_table_as_sql(name, sql, options) # http://www.ibm.com/developerworks/data/library/techarticle/dm-0912globaltemptable/ def create_table_prefix_sql(name, options) if options[:temp] - "DECLARE GLOBAL TEMPORARY TABLE #{quote_identifier(name)}" + "DECLARE GLOBAL TEMPORARY TABLE #{create_table_table_name_sql(name, options)}" else super end diff --git a/lib/sequel/adapters/shared/mssql.rb b/lib/sequel/adapters/shared/mssql.rb index e459d3e9d..d8947df59 100644 --- a/lib/sequel/adapters/shared/mssql.rb +++ b/lib/sequel/adapters/shared/mssql.rb @@ -379,9 +379,21 @@ def commit_transaction_sql # a regular and temporary table, with temporary table names starting with # a #. def create_table_prefix_sql(name, options) - "CREATE TABLE #{quote_schema_table(options[:temp] ? "##{name}" : name)}" + "CREATE TABLE #{create_table_table_name_sql(name, options)}" end - + + # The SQL to use for the table name for a temporary table. + def create_table_temp_table_name_sql(name, _options) + case name + when String, Symbol + "##{name}" + when SQL::Identifier + "##{name.value}" + else + raise Error, "temporary table names must be strings, symbols, or Sequel::SQL::Identifier instances on Microsoft SQL Server" + end + end + # MSSQL doesn't support CREATE TABLE AS, it only supports SELECT INTO. # Emulating CREATE TABLE AS using SELECT INTO is only possible if a dataset # is given as the argument, it can't work with a string, so raise an diff --git a/lib/sequel/adapters/shared/postgres.rb b/lib/sequel/adapters/shared/postgres.rb index 4087ebba2..cda8e1a94 100644 --- a/lib/sequel/adapters/shared/postgres.rb +++ b/lib/sequel/adapters/shared/postgres.rb @@ -1429,7 +1429,7 @@ def create_table_prefix_sql(name, options) 'UNLOGGED ' end - "CREATE #{prefix_sql}TABLE#{' IF NOT EXISTS' if options[:if_not_exists]} #{options[:temp] ? quote_identifier(name) : quote_schema_table(name)}" + "CREATE #{prefix_sql}TABLE#{' IF NOT EXISTS' if options[:if_not_exists]} #{create_table_table_name_sql(name, options)}" end # SQL for creating a table with PostgreSQL specific options diff --git a/lib/sequel/database/schema_methods.rb b/lib/sequel/database/schema_methods.rb index 61a07784c..c7cdd0c9f 100644 --- a/lib/sequel/database/schema_methods.rb +++ b/lib/sequel/database/schema_methods.rb @@ -775,7 +775,21 @@ def create_table_as_sql(name, sql, options) # SQL fragment for initial part of CREATE TABLE statement def create_table_prefix_sql(name, options) - "CREATE #{temporary_table_sql if options[:temp]}TABLE#{' IF NOT EXISTS' if options[:if_not_exists]} #{options[:temp] ? quote_identifier(name) : quote_schema_table(name)}" + "CREATE #{temporary_table_sql if options[:temp]}TABLE#{' IF NOT EXISTS' if options[:if_not_exists]} #{create_table_table_name_sql(name, options)}" + end + + # The SQL to use for a table name when creating a table. + # Use of the :temp option can result in different SQL, + # because the rules for temp table naming can differ + # between databases, and temp tables should not use the + # default_schema. + def create_table_table_name_sql(name, options) + options[:temp] ? create_table_temp_table_name_sql(name, options) : quote_schema_table(name) + end + + # The SQL to use for the table name for a temporary table. + def create_table_temp_table_name_sql(name, _options) + name.is_a?(String) ? quote_identifier(name) : literal(name) end # SQL fragment for initial part of CREATE VIEW statement diff --git a/spec/core/schema_spec.rb b/spec/core/schema_spec.rb index 21158f481..12e009a9b 100644 --- a/spec/core/schema_spec.rb +++ b/spec/core/schema_spec.rb @@ -1445,6 +1445,17 @@ def @db.foreign_key_list(table_name) @db.sqls.must_equal ['CREATE TEMPORARY TABLE test_tmp (id integer NOT NULL PRIMARY KEY AUTOINCREMENT, name text)', 'CREATE UNIQUE INDEX test_tmp_name_index ON test_tmp (name)'] end + + it "should create a schema-qualified temporary table" do + @db.create_table Sequel[:sch][:test_tmp], :temp => true do + primary_key :id, :integer, :null => false + column :name, :text + index :name, :unique => true + end + + @db.sqls.must_equal ['CREATE TEMPORARY TABLE sch.test_tmp (id integer NOT NULL PRIMARY KEY AUTOINCREMENT, name text)', + 'CREATE UNIQUE INDEX sch_test_tmp_name_index ON sch.test_tmp (name)'] + end end describe "Database#alter_table" do diff --git a/spec/integration/schema_test.rb b/spec/integration/schema_test.rb index 0b077a57a..18507c431 100644 --- a/spec/integration/schema_test.rb +++ b/spec/integration/schema_test.rb @@ -384,13 +384,32 @@ end if DB.supports_foreign_key_parsing? describe "Database" do - after do + before do DB.drop_table(:items_temp) rescue nil end - - it "should create temporary tables without raising an exception" do + after(:all) do DB.disconnect - DB.create_table!(:items_temp, :temp=>true){Integer :number} + end + + it "should allow creation and use of temporary tables" do + DB.synchronize do + DB.create_table!(:items_temp, :temp=>true){Integer :number} + table = case DB.database_type + when :mssql + Sequel.lit("#items_temp") + when :derby + Sequel[:session][:items_temp] + else + :items_temp + end + DB.from(table).all.must_equal [] + end + end + + it "should raise when trying to create a schema qualified temporary table" do + proc do + DB.create_table(Sequel[:invalid_temp_schema][:items_temp], :temp=>true){Integer :number} + end.must_raise(DB.database_type == :mssql ? Sequel::Error : Sequel::DatabaseError) end end