From b3e0da3062f809599fb4da9f9a29ed45aebd1ff6 Mon Sep 17 00:00:00 2001 From: lsylvester Date: Tue, 11 Mar 2014 08:10:25 +1100 Subject: register OID for PostgreSQL citex datatype [Troy Kruthoff & Lachlan Sylvester] citext makes it possible to use AR Hash finders for case-insensitive matching as sql UPPER/LOWER functions are not needed. --- activerecord/CHANGELOG.md | 5 ++ .../connection_adapters/postgresql/oid.rb | 1 + .../connection_adapters/postgresql_adapter.rb | 9 +++- .../test/cases/adapters/postgresql/citext_test.rb | 58 ++++++++++++++++++++++ activerecord/test/cases/schema_dumper_test.rb | 7 +++ .../test/schema/postgresql_specific_schema.rb | 11 +++- 6 files changed, 89 insertions(+), 2 deletions(-) create mode 100644 activerecord/test/cases/adapters/postgresql/citext_test.rb diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index 300c3761f1..2e4f653ea6 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,8 @@ +* Support for Postgres `citext` data type enabling case-insensitive where + values without needing to wrap in UPPER/LOWER sql functions. + + *Troy Kruthoff*, *Lachlan Sylvester* + * Only save has_one associations if record has changes. Previously after save related callbacks, such as `#after_commit`, were triggered when the has_one object did not get saved to the db. diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb index 697915f3e9..5d32aaed50 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb @@ -375,6 +375,7 @@ This is not reliable and will be removed in the future. register_type 'circle', OID::Text.new register_type 'hstore', OID::Hstore.new register_type 'json', OID::Json.new + register_type 'citext', OID::Text.new register_type 'ltree', OID::Text.new register_type 'cidr', OID::Cidr.new diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index a56ef91d07..f7b053aec6 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -215,6 +215,8 @@ module ActiveRecord # Character types when /^(?:character varying|bpchar)(?:\(\d+\))?$/ :string + when /^citext(?:\(\d+\))?$/ + :citext # Binary data types when 'bytea' :binary @@ -393,6 +395,10 @@ module ActiveRecord column name, type, options end + def citext(name, options = {}) + column(name, 'citext', options) + end + def column(name, type = nil, options = {}) super column = self[name] @@ -441,7 +447,8 @@ module ActiveRecord macaddr: { name: "macaddr" }, uuid: { name: "uuid" }, json: { name: "json" }, - ltree: { name: "ltree" } + ltree: { name: "ltree" }, + citext: { name: "citext" } } include Quoting diff --git a/activerecord/test/cases/adapters/postgresql/citext_test.rb b/activerecord/test/cases/adapters/postgresql/citext_test.rb new file mode 100644 index 0000000000..89ef46b539 --- /dev/null +++ b/activerecord/test/cases/adapters/postgresql/citext_test.rb @@ -0,0 +1,58 @@ +# encoding: utf-8 + +require 'cases/helper' +require 'active_record/base' +require 'active_record/connection_adapters/postgresql_adapter' + +class PostgresqlCitextTest < ActiveRecord::TestCase + class Citext < ActiveRecord::Base + self.table_name = 'citexts' + end + + def setup + @connection = ActiveRecord::Base.connection + + unless @connection.extension_enabled?('citext') + @connection.enable_extension 'citext' + @connection.commit_db_transaction + end + + @connection.reconnect! + + @connection.transaction do + @connection.create_table('citexts') do |t| + t.citext 'cival' + end + end + @column = Citext.columns_hash['cival'] + end + + def teardown + @connection.execute 'DROP TABLE IF EXISTS citexts;' + @connection.execute 'DROP EXTENSION IF EXISTS citext CASCADE;' + end + + def test_citext_enabled + assert @connection.extension_enabled?('citext') + end + + def test_column_type + assert_equal :citext, @column.type + end + + def test_column_sql_type + assert_equal 'citext', @column.sql_type + end + + def test_write + x = Citext.new(cival: 'Some CI Text') + assert x.save! + end + + def test_select_case_insensitive + @connection.execute "insert into citexts (cival) values('Cased Text')" + x = Citext.where(cival: 'cased text').first + assert_equal('Cased Text', x.cival) + end + +end \ No newline at end of file diff --git a/activerecord/test/cases/schema_dumper_test.rb b/activerecord/test/cases/schema_dumper_test.rb index 2748cbdbf4..575eb34a9c 100644 --- a/activerecord/test/cases/schema_dumper_test.rb +++ b/activerecord/test/cases/schema_dumper_test.rb @@ -319,6 +319,13 @@ class SchemaDumperTest < ActiveRecord::TestCase end end + def test_schema_dump_includes_citext_shorthand_definition + output = standard_dump + if %r{create_table "postgresql_citext"} =~ output + assert_match %r[t.citext "text_citext"], output + end + end + def test_schema_dump_includes_ltrees_shorthand_definition output = standard_dump if %r{create_table "postgresql_ltrees"} =~ output diff --git a/activerecord/test/schema/postgresql_specific_schema.rb b/activerecord/test/schema/postgresql_specific_schema.rb index a86a188bcf..4fcbf4dbd2 100644 --- a/activerecord/test/schema/postgresql_specific_schema.rb +++ b/activerecord/test/schema/postgresql_specific_schema.rb @@ -1,7 +1,7 @@ ActiveRecord::Schema.define do %w(postgresql_tsvectors postgresql_hstores postgresql_arrays postgresql_moneys postgresql_numbers postgresql_times postgresql_network_addresses postgresql_bit_strings postgresql_uuids postgresql_ltrees - postgresql_oids postgresql_xml_data_type defaults geometrics postgresql_timestamp_with_zones postgresql_partitioned_table postgresql_partitioned_table_parent postgresql_json_data_type).each do |table_name| + postgresql_oids postgresql_xml_data_type defaults geometrics postgresql_timestamp_with_zones postgresql_partitioned_table postgresql_partitioned_table_parent postgresql_json_data_type postgresql_citext).each do |table_name| execute "DROP TABLE IF EXISTS #{quote_table_name table_name}" end @@ -99,6 +99,15 @@ _SQL _SQL end + if 't' == select_value("select 'citext'=ANY(select typname from pg_type)") + execute <<_SQL + CREATE TABLE postgresql_citext ( + id SERIAL PRIMARY KEY, + text_citext citext default ''::citext + ); +_SQL + end + if 't' == select_value("select 'json'=ANY(select typname from pg_type)") execute <<_SQL CREATE TABLE postgresql_json_data_type ( -- cgit v1.2.3