Skip to content

Commit 7248f4a

Browse files
committed
Postgres producer ALTER COLUMN: specify USING for datatype change
Postgres 8 and later require a C<< USING expression >> clause when changing the type of a column in a potentially lossy manner, such as from C<text> to C<numeric>. This change adds an automatic "USING (colname::datatype)" suffix any time the data type has changed, which should be a safe default. It is also conditioned on the version of Postgres, but also makes it the default in absence of a version, since Pg 8 is so old.
1 parent 18be6d1 commit 7248f4a

4 files changed

Lines changed: 27 additions & 17 deletions

File tree

lib/SQL/Translator/Producer/PostgreSQL.pm

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,12 @@ The version of postgres to generate DDL for. Turns on features only available in
3535
If your postgres_version is higher than 8.003 (I should hope it is by now), then the DDL
3636
generated for dropping objects in the database will contain IF EXISTS.
3737
38+
=item ALTER COLUMN ... TYPE ... USING
39+
40+
Postgres 8 and later require a C<< USING expression >> clause when changing the type of a column
41+
in a potentially lossy manner, such as from C<text> to C<numeric>. This the default when the
42+
version is undefined, or when set to a number greater or equal to 8.
43+
3844
=back
3945
4046
=item attach_comments
@@ -910,27 +916,31 @@ sub alter_field {
910916
# rename later
911917
# BUT: drop geometry is done before the rename, cause it work's on the
912918
# $from_field directly
919+
my $to_table_quoted= $generator->quote($to_field->table->name);
920+
my $to_field_quoted= $generator->quote($to_field->name);
913921
push @out,
914922
sprintf('ALTER TABLE %s RENAME COLUMN %s TO %s',
915-
map($generator->quote($_), $to_field->table->name, $from_field->name, $to_field->name,),)
923+
$to_table_quoted, $generator->quote($from_field->name), $to_field_quoted)
916924
if ($from_field->name ne $to_field->name);
917925

918926
push @out,
919927
sprintf('ALTER TABLE %s ALTER COLUMN %s SET NOT NULL',
920-
map($generator->quote($_), $to_field->table->name, $to_field->name),)
928+
$to_table_quoted, $to_field_quoted)
921929
if (!$to_field->is_nullable and $from_field->is_nullable);
922930

923931
push @out,
924932
sprintf('ALTER TABLE %s ALTER COLUMN %s DROP NOT NULL',
925-
map($generator->quote($_), $to_field->table->name, $to_field->name),)
933+
$to_table_quoted, $to_field_quoted)
926934
if (!$from_field->is_nullable and $to_field->is_nullable);
927935

928936
my $from_dt = convert_datatype($from_field);
929937
my $to_dt = convert_datatype($to_field);
930-
push @out,
931-
sprintf('ALTER TABLE %s ALTER COLUMN %s TYPE %s',
932-
map($generator->quote($_), $to_field->table->name, $to_field->name), $to_dt,)
933-
if ($to_dt ne $from_dt);
938+
push @out, ($options->{postgres_version}//8) < 8
939+
? sprintf('ALTER TABLE %s ALTER COLUMN %s TYPE %s',
940+
$to_table_quoted, $to_field_quoted, $to_dt)
941+
: sprintf('ALTER TABLE %s ALTER COLUMN %s TYPE %s USING (%s::%s)',
942+
$to_table_quoted, $to_field_quoted, $to_dt, $to_field_quoted, $to_dt)
943+
if $to_dt ne $from_dt;
934944

935945
my ($from_enum_typename, $from_list) = _enum_typename_and_values($from_field);
936946
my ($to_enum_typename, $to_list) = _enum_typename_and_values($to_field);

t/30sqlt-new-diff-pgsql.t

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -72,19 +72,19 @@ DROP INDEX "u_name";
7272
7373
ALTER TABLE "person" ADD COLUMN "is_rock_star" smallint DEFAULT 1;
7474
75-
ALTER TABLE "person" ALTER COLUMN "person_id" TYPE serial;
75+
ALTER TABLE "person" ALTER COLUMN "person_id" TYPE serial USING ("person_id"::serial);
7676
7777
ALTER TABLE "person" ALTER COLUMN "name" SET NOT NULL;
7878
7979
ALTER TABLE "person" ALTER COLUMN "age" SET DEFAULT 18;
8080
8181
ALTER TABLE "person" ALTER COLUMN "weight" DROP NOT NULL;
8282
83-
ALTER TABLE "person" ALTER COLUMN "iq" TYPE bigint;
83+
ALTER TABLE "person" ALTER COLUMN "iq" TYPE bigint USING ("iq"::bigint);
8484
8585
ALTER TABLE "person" ALTER COLUMN "nickname" SET NOT NULL;
8686
87-
ALTER TABLE "person" ALTER COLUMN "nickname" TYPE character varying(24);
87+
ALTER TABLE "person" ALTER COLUMN "nickname" TYPE character varying(24) USING ("nickname"::character varying(24));
8888
8989
ALTER TABLE "person" RENAME COLUMN "description" TO "physical_description";
9090
@@ -134,19 +134,19 @@ ALTER TABLE person DROP CONSTRAINT UC_age_name;
134134
135135
ALTER TABLE person ADD COLUMN is_rock_star smallint DEFAULT 1;
136136
137-
ALTER TABLE person ALTER COLUMN person_id TYPE serial;
137+
ALTER TABLE person ALTER COLUMN person_id TYPE serial USING (person_id::serial);
138138
139139
ALTER TABLE person ALTER COLUMN name SET NOT NULL;
140140
141141
ALTER TABLE person ALTER COLUMN age SET DEFAULT 18;
142142
143143
ALTER TABLE person ALTER COLUMN weight DROP NOT NULL;
144144
145-
ALTER TABLE person ALTER COLUMN iq TYPE bigint;
145+
ALTER TABLE person ALTER COLUMN iq TYPE bigint USING (iq::bigint);
146146
147147
ALTER TABLE person ALTER COLUMN nickname SET NOT NULL;
148148
149-
ALTER TABLE person ALTER COLUMN nickname TYPE character varying(24);
149+
ALTER TABLE person ALTER COLUMN nickname TYPE character varying(24) USING (nickname::character varying(24));
150150
151151
ALTER TABLE person RENAME COLUMN description TO physical_description;
152152

t/47postgres-producer.t

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -264,7 +264,7 @@ subtest 'exclude constraints' => sub {
264264
my $alter_field = SQL::Translator::Producer::PostgreSQL::alter_field($field1, $field2);
265265
is(
266266
$alter_field, qq[ALTER TABLE mytable ALTER COLUMN myfield SET NOT NULL;
267-
ALTER TABLE mytable ALTER COLUMN myfield TYPE character varying(25)],
267+
ALTER TABLE mytable ALTER COLUMN myfield TYPE character varying(25) USING (myfield::character varying(25))],
268268
'Alter field works'
269269
);
270270

@@ -296,7 +296,7 @@ my $alter_field_complex = SQL::Translator::Producer::PostgreSQL::alter_field($fi
296296
is(
297297
$alter_field_complex,
298298
q{ALTER TABLE mytable RENAME COLUMN my_complex_field TO my_altered_field;
299-
ALTER TABLE mytable ALTER COLUMN my_altered_field TYPE character varying(60);
299+
ALTER TABLE mytable ALTER COLUMN my_altered_field TYPE character varying(60) USING (my_altered_field::character varying(60));
300300
ALTER TABLE mytable ALTER COLUMN my_altered_field SET DEFAULT 'whatever'},
301301
'Complex Alter field works'
302302
);

t/63-spacial-pgsql.t

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,15 +83,15 @@ ALTER TABLE "my'table" DROP CONSTRAINT "enforce_dims_myfield";
8383
ALTER TABLE "my'table" DROP CONSTRAINT "enforce_srid_myfield";
8484
ALTER TABLE "my'table" DROP CONSTRAINT "enforce_geotype_myfield";
8585
ALTER TABLE "my'table" ALTER COLUMN "myfield" SET NOT NULL;
86-
ALTER TABLE "my'table" ALTER COLUMN "myfield" TYPE character varying(25)],
86+
ALTER TABLE "my'table" ALTER COLUMN "myfield" TYPE character varying(25) USING ("myfield"::character varying(25))],
8787
'Alter field geometry to non geometry works'
8888
);
8989

9090
my $alter_field2 = SQL::Translator::Producer::PostgreSQL::alter_field($field2, $field1, $options);
9191
is(
9292
$alter_field2,
9393
qq[ALTER TABLE "my'table" ALTER COLUMN "myfield" DROP NOT NULL;
94-
ALTER TABLE "my'table" ALTER COLUMN "myfield" TYPE geometry;
94+
ALTER TABLE "my'table" ALTER COLUMN "myfield" TYPE geometry USING ("myfield"::geometry);
9595
INSERT INTO geometry_columns VALUES ('','myschema','my''table','myfield','2','-1','POINT');
9696
ALTER TABLE "my'table" ADD CONSTRAINT "enforce_dims_myfield" CHECK ((ST_NDims("myfield") = 2));
9797
ALTER TABLE "my'table" ADD CONSTRAINT "enforce_srid_myfield" CHECK ((ST_SRID("myfield") = -1));

0 commit comments

Comments
 (0)