Skip to content

Add support for PostgreSQL database #1034

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
15 tasks done
php-coder opened this issue Jun 5, 2019 · 22 comments
Closed
15 tasks done

Add support for PostgreSQL database #1034

php-coder opened this issue Jun 5, 2019 · 22 comments
Assignees
Labels
area/infrastructure impact/changelog This change should be reflected in the NEWS.txt file kind/feature
Milestone

Comments

@php-coder
Copy link
Owner

php-coder commented Jun 5, 2019

  • adapt application to work with PostgreSQL
  • fix the migrations
  • review and fix the sequences
    • users_id_seq
    • series_id_seq
    • collections_id_seq
    • transaction_participants_id_seq
    • series_sales_id_seq
    • site_parsers_id_seq
    • images
    • *_catalog
  • review and ensure that we don't insert ids with prod profile
  • run integration tests on PostgreSQL as well (extracted into Travis CI: run tests on PostgreSQL #1054)
  • add separate docker-compose file
  • update README.md/NEWS.txt files
@php-coder
Copy link
Owner Author

It doesn't start because one of migrations fails:

2019-06-06 01:32:03.476 WARN 1 --- [ main] ationConfigEmbeddedWebApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'liquibase' defined in class path resource [org/springframework/boot/autoconfigure/liquibase/LiquibaseAutoConfiguration$LiquibaseConfiguration.class]: Invocation of init method failed; nested exception is liquibase.exception.MigrationFailedException: Migration failed for change set classpath:/liquibase/version/0.3/2014-02-11--categories.xml::initiate-categories-table::php-coder:
Reason: liquibase.exception.DatabaseException: ERROR: syntax error at or near "$"
Position: 162 [Failed SQL: INSERT INTO public.categories (id, name, name_ru, created_at, created_by, updated_at, updated_by) VALUES ('1', 'Prehistoric animals', 'Доисторические животные', ${NOW}, '2', ${NOW}, '2')]

@php-coder php-coder modified the milestones: 0.4.1, 0.4 Jun 5, 2019
@php-coder php-coder self-assigned this Jun 5, 2019
@php-coder
Copy link
Owner Author

UPDATE: this error happens only when "test-data" context is active in Liquibase (that means that the test and travis profiles should be affected by that).

@php-coder
Copy link
Owner Author

After fixing the previous issue (by declaring NOW property properly), the new issue appear:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'liquibase' defined in class path resource [org/springframework/boot/autoconfigure/liquibase/LiquibaseAutoConfigura
tion$LiquibaseConfiguration.class]: Invocation of init method failed; nested exception is liquibase.exception.MigrationFailedException: Migration failed for change set classpath:/liquibase/version/0.3/2
014-05-28--release_month_and_day.xml::fill-release_year-column-based-on-values-from-released_at::php-coder:
Reason: liquibase.exception.DatabaseException: ERROR: function year(date) does not exist
Hint: No function matches the given name and argument types. You might need to add explicit type casts.
Position: 35 [Failed SQL: UPDATE series
SET release_year=YEAR(released_at)
WHERE released_at IS NOT NULL]

php-coder added a commit that referenced this issue Jun 5, 2019
This change adds 661Kb to the WAR file.

Addressed to #1034
php-coder added a commit that referenced this issue Jun 5, 2019
The failure was:
liquibase.exception.DatabaseException: ERROR: syntax error at or near "$"
Position: 162 [Failed SQL: INSERT INTO public.categories
(id, name, name_ru, created_at, created_by, updated_at, updated_by)
VALUES ('1', 'Prehistoric animals', 'Доисторические животные', ${NOW}, '2', ${NOW}, '2')]

Addressed to #1034
@php-coder
Copy link
Owner Author

The next error is:

Caused by: liquibase.exception.MigrationFailedException: Migration failed for change set classpath:/liquibase/version/0.3/2014-09-28--collection_slug.xml::update-collections-slug::php-coder:
Reason: liquibase.exception.DatabaseException: ERROR: column "c" of relation "collections" does not exist
Position: 29 [Failed SQL: UPDATE collections c
SET c.slug = (
SELECT LOWER(REPLACE(u.login, ' ', '-'))
FROM users u
WHERE u.id = c.user_id
)]

@php-coder
Copy link
Owner Author

The next error is:

Caused by: liquibase.exception.MigrationFailedException: Migration failed for change set classpath:/liquibase/version/0.4/2015-06-22--image_url.xml::fill-series_images-table-based-on-values-from-image_url::php-coder:
Reason: liquibase.exception.DatabaseException: ERROR: column "image_id" is of type integer but expression is of type text
Hint: You will need to rewrite or cast the expression.
Position: 75 [Failed SQL: INSERT INTO series_images(series_id, image_id)
SELECT id AS series_id, REPLACE(image_url, '/image/', '') AS image_id
FROM series]

@php-coder
Copy link
Owner Author

One more:

Caused by: liquibase.exception.MigrationFailedException: Migration failed for change set classpath:/liquibase/version/0.4/2015-07-07--salt_and_hash.xml::increase-length-of-hash-column-in-users-table::php-coder:
Reason: liquibase.exception.DatabaseException: ERROR: syntax error at or near "MODIFY"
Position: 22 [Failed SQL: ALTER TABLE users
MODIFY COLUMN hash VARCHAR(60) NOT NULL]

@php-coder
Copy link
Owner Author

Yet another:

Caused by: liquibase.exception.MigrationFailedException: Migration failed for change set classpath:/liquibase/version/0.4/2016-01-14--unique_slug_in_countries.xml::add-unique-constraint-to-slug-in-countries-table::AleksSPb:
Reason:
classpath:/liquibase/changelog.xml : liquibase.precondition.core.SqlPrecondition@74bdc168 : ERROR: column "num" does not exist
Position: 72
...
Caused by: liquibase.exception.DatabaseException: Error executing SQL SELECT COUNT(*) as num
FROM countries
GROUP BY slug
HAVING num > 1
UNION SELECT 0: ERROR: column "num" does not exist

@php-coder
Copy link
Owner Author

The same but for categories:

Caused by: liquibase.exception.MigrationFailedException: Migration failed for change set classpath:/liquibase/version/0.4/2016-07-22--unique_slug_in_categories.xml::add-unique-constraint-to-slug-in-categories-table::php-coder:
Reason:
classpath:/liquibase/changelog.xml : liquibase.precondition.core.SqlPrecondition@290b1b2e : ERROR: column "num" does not exist
Position: 73
...
Caused by: liquibase.exception.PreconditionErrorException: Precondition Error
at liquibase.precondition.core.SqlPrecondition.check(SqlPrecondition.java:64) ~[liquibase-core-3.5.5.jar!/:na]
at liquibase.precondition.core.AndPrecondition.check(AndPrecondition.java:43) ~[liquibase-core-3.5.5.jar!/:na]
at liquibase.precondition.core.PreconditionContainer.check(PreconditionContainer.java:202) ~[liquibase-core-3.5.5.jar!/:na]
at liquibase.changelog.ChangeSet.execute(ChangeSet.java:498) ~[liquibase-core-3.5.5.jar!/:na]
... 34 common frames omitted
Caused by: liquibase.exception.DatabaseException: Error executing SQL SELECT COUNT(*) as num
FROM categories
GROUP BY slug
HAVING num > 1
UNION SELECT 0: ERROR: column "num" does not exist
Position: 73

@php-coder
Copy link
Owner Author

Also there is another one in 0.4/2016-08-18--unique_slug_in_collections.xml::add-unique-constraint-to-slug-in-collections-table but for collections.

@php-coder
Copy link
Owner Author

Now it fails with:

Caused by: liquibase.exception.MigrationFailedException: Migration failed for change set classpath:/liquibase/version/0.4/2017-01-06--top_categories.xml::add-sport-category::php-coder:
Reason: liquibase.exception.DatabaseException: ERROR: duplicate key value violates unique constraint "pk_categories"
Detail: Key (id)=(1) already exists. [Failed SQL: INSERT INTO public.categories (name, name_ru, slug, created_at, created_by, updated_at, updated_by) VALUES ('Sport', '?????', 'sport', NOW(), (SELECT id FROM users WHERE role = 'ADMIN' ORDER by id LIMIT 1), NOW(), (SELECT id FROM users WHERE role = 'ADMIN' ORDER by id LIMIT 1))]

php-coder added a commit that referenced this issue Jun 6, 2019
… field) instead of YEAR() on PostgreSQL.

The failure was:
liquibase.exception.DatabaseException: ERROR: function year(date) does not exist
Hint: No function matches the given name and argument types. You might need to add explicit type casts.
Position: 35 [Failed SQL: UPDATE series SET release_year=YEAR(released_at) WHERE released_at IS NOT NULL]

Addressed to #1034
php-coder added a commit that referenced this issue Jun 6, 2019
…table alias in SET clause on PostgreSQL.

The failure was:
liquibase.exception.DatabaseException: ERROR: column "c" of relation "collections" does not exist
Position: 29 [Failed SQL: UPDATE collections c SET c.slug = ( SELECT LOWER(REPLACE(u.login, ' ', '-')) FROM users u WHERE u.id = c.user_id) ]

See also: https://stackoverflow.com/questions/11369757/postgres-wont-accept-table-alias-before-column-name

Addressed to #1034
php-coder added a commit that referenced this issue Jun 6, 2019
…teger on PostgreSQL.

The failure was:
liquibase.exception.DatabaseException: ERROR: column "image_id" is of type integer but expression is of type text
Hint: You will need to rewrite or cast the expression.
Position: 75 [Failed SQL: INSERT INTO series_images(series_id, image_id) SELECT id AS series_id, REPLACE(image_url, '/image/', '') AS image_id FROM series]

Addressed to #1034
php-coder added a commit that referenced this issue Jun 6, 2019
…on PostgreSQL.

The failure was:
liquibase.exception.DatabaseException: ERROR: syntax error at or near "MODIFY"
Position: 22 [Failed SQL: ALTER TABLE users MODIFY COLUMN hash VARCHAR(60) NOT NULL]

Addressed to #1034
php-coder added a commit that referenced this issue Jun 6, 2019
…clause to fix precondition on PostgreSQL.

The failures were like the following:
MigrationFailedException: Migration failed for change set classpath:/liquibase/version/0.4/2016-01-14--unique_slug_in_countries.xml::add-unique-constraint-to-slug-in-countries-table::AleksSPb:
Reason:
classpath:/liquibase/changelog.xml : liquibase.precondition.core.SqlPrecondition@74bdc168 : ERROR: column "num" does not exist
Position: 72
...
Caused by: liquibase.exception.DatabaseException: Error executing SQL SELECT COUNT(*) as num FROM countries GROUP BY slug HAVING num > 1 UNION SELECT 0:
ERROR: column "num" does not exist

Addressed to #1034
php-coder added a commit that referenced this issue Jun 9, 2019
… id instead of providing our own.

This change should fix 2017-01-06--top_categories.xml migration on
PostgreSQL. Prior this commit, we were inserting 'Prehistoric animals'
category with explicitly provided id=1 By doing this, we didn't update
the sequence generator. When next time we were trying to add another
category without id, the sequence generator gave us the value 1 and
migration were failing as a category with such id already exist.

See for details:
https://stackoverflow.com/questions/44744365/liquibase-postgresql-spring-jpa-id-auto-increment-issue

There were two ways to fix this: always specify id explicitly and adjust
the sequence generator manually after that or never provide an id and
let the generator do all the work for us. This change implements the
second approach because the first one would be specific to PostgreSQL
(as on MySQL we don't need to adjust generators at all).

The failure was:
MigrationFailedException: Migration failed for change set classpath:/liquibase/version/0.4/2017-01-06--top_categories.xml::add-sport-category::php-coder:
Reason: liquibase.exception.DatabaseException: ERROR: duplicate key value violates unique constraint "pk_categories"
Detail: Key (id)=(1) already exists.
[Failed SQL: INSERT INTO public.categories (name, name_ru, slug, created_at, created_by, updated_at, updated_by)
VALUES ('Sport', 'Спорт', 'sport', NOW(), (SELECT id FROM users WHERE role = 'ADMIN' ORDER by id LIMIT 1), NOW(),
(SELECT id FROM users WHERE role = 'ADMIN' ORDER by id LIMIT 1))]

Addressed to #1034
@php-coder
Copy link
Owner Author

One more:

Caused by: liquibase.exception.MigrationFailedException: Migration failed for change set classpath:/liquibase/version/0.4/2017-05-29--test_image.xml::add-test-image::php-coder:
Reason: liquibase.exception.DatabaseException: org.postgresql.util.PSQLException: ERROR: column "image_id" is of type integer but expression is of type character varying
Hint: You will need to rewrite or cast the expression.
Position: 58

php-coder added a commit that referenced this issue Jun 9, 2019
…migration on PostgreSQL.

The failure was:
MigrationFailedException: Migration failed for change set classpath:/liquibase/version/0.4/2017-05-29--test_image.xml::add-test-image::php-coder:
Reason: liquibase.exception.DatabaseException: org.postgresql.util.PSQLException:
ERROR: column "image_id" is of type integer but expression is of type character varying
Hint: You will need to rewrite or cast the expression.

Addressed to #1034
@php-coder
Copy link
Owner Author

More errors, this time related to our usage of JdbcTemplate:

org.springframework.dao.InvalidDataAccessApiUsageException: The getKey method should only be used when a single key is returned. The current key entry contains multiple keys: [{id=4, name=test, created_at=2019-06-09 21:14:26.889, created_by=2, updated_at=2019-06-09 21:14:26.889, updated_by=2, name_ru=null, slug=test}]

php-coder added a commit that referenced this issue Jun 9, 2019
…essApiUsageException on PostgreSQL.

See: https://stackoverflow.com/questions/17771306/spring-how-to-use-keyholder-with-postgresql

The errors were like the following:
org.springframework.dao.InvalidDataAccessApiUsageException:
The getKey method should only be used when a single key is returned.
The current key entry contains multiple keys:
[{id=4, name=test, created_at=2019-06-09 21:14:26.889, created_by=2, updated_at=2019-06-09 21:14:26.889, updated_by=2, name_ru=null, slug=test}]

Addressed to #1034
@php-coder php-coder added the impact/changelog This change should be reflected in the NEWS.txt file label Jun 9, 2019
php-coder added a commit that referenced this issue Jun 12, 2019
How ro run:
$ cd docker
$ docker-compose -f docker-compose.yml -f postgres.yml up -d

Addressed to #1034

[skip ci]
@0pdd
Copy link

0pdd commented Jun 12, 2019

@php-coder the puzzle #1039 is still not solved.

@php-coder
Copy link
Owner Author

The following sequences have to be fixed:

  • users_id_seq
  • series_id_seq
  • collections_id_seq
  • transaction_participants_id_seq
  • series_sales_id_seq
  • site_parsers_id_seq

Here is how to test that it's OK:

mystamps=# SELECT 'zagorski_catalog_id_seq', (SELECT MAX(id) FROM zagorski_catalog), last_value FROM zagorski_catalog_id_seq;
        ?column?         | max | last_value
-------------------------+-----+------------
 zagorski_catalog_id_seq |   1 |          1
(1 row)

The max and last_value must have the same value.

Here how it looks when it doesn't work:

mystamps=# SELECT 'users_id_seq', (SELECT MAX(id) FROM users), last_value FROM users_id_seq;
   ?column?   | max | last_value
--------------+-----+------------
 users_id_seq |   5 |          1
(1 row)
mystamps=# insert into users(login,role,name,email,hash,registered_at,activated_at) values('test','USER','TEST','[email protected]','XXX',NOW(),NOW());
ERROR:  duplicate key value violates unique constraint "pk_users"
DETAIL:  Key (id)=(1) already exists.

php-coder added a commit that referenced this issue Jun 12, 2019
…lations on insertion later.

See cdb000e commit for details.

Addressed to #1034
php-coder added a commit that referenced this issue Jun 12, 2019
…lations on insertion later.

See cdb000e commit for details.

Addressed to #1034
php-coder added a commit that referenced this issue Jun 12, 2019
…cate key violations on insertion later.

See cdb000e commit for details.

Addressed to #1034
php-coder added a commit that referenced this issue Jun 12, 2019
…ations on insertion later.

See cdb000e commit for details.

Addressed to #1034
php-coder added a commit that referenced this issue Jun 12, 2019
…ions on insertion later.

See cdb000e commit for details.

Addressed to #1034
@php-coder
Copy link
Owner Author

Some of the fixes broke change checksums check:

     5 change sets check sum
          classpath:/liquibase/version/0.3/2014-05-28--release_month_and_day.xml::fill-release_year-column-based-on-values-from-released_at::php-coder was: 7:9d233932224649aa730fd82df77f22a4 but is now: 7:809596648b6e7ef5de74d5c690611f53
          classpath:/liquibase/version/0.3/2014-09-28--collection_slug.xml::update-collections-slug::php-coder was: 7:e097ed4a1bcc0a584aa5b385cff2d03a but is now: 7:8af814be468761d310c0a46c5f5cfbbf
          classpath:/liquibase/version/0.4/2015-06-22--image_url.xml::fill-series_images-table-based-on-values-from-image_url::php-coder was: 7:b0d069aacd68b75cacdc22a6c0fb9503 but is now: 7:abff5f789b843a3131a331f9e99ce92c
          classpath:/liquibase/version/0.4/2015-07-07--salt_and_hash.xml::increase-length-of-hash-column-in-users-table::php-coder was: 7:4160c036a7d7c4cae76ce701d8c4e52a but is now: 7:85d6c91356b5832a281669e7ec0e217c
          classpath:/liquibase/version/0.4/2018-06-09--add_number_of_stamps_to_collections_series.xml::fill-collections_series-number_of_stamps-field::php-coder was: 7:acf5541cb113870c46be460417935cf9 but is now: 7:1333048e062daba328f7af56a3ec9c61

php-coder added a commit that referenced this issue Jun 13, 2019
During adaptation for PostgreSQL, the modifications were made on the existing changesets. It has
made them failing on existing data as the checksums were changed. In the new approach, we moved
conditions from statements within a changeset to a changeset itself.

Correction for the following commits:
- c03b2cc
- 6d296fd
- 7282a7b
- d767dab
- 11c898b

Addressed to #1034
@php-coder php-coder pinned this issue Jun 15, 2019
php-coder added a commit that referenced this issue Jun 16, 2019
…s on insertion later.

See cdb000e commit for details.

Addressed to #1034
php-coder added a commit that referenced this issue Jun 16, 2019
…s on insertion later.

See cdb000e commit for details.

Addressed to #1034
php-coder added a commit that referenced this issue Jun 16, 2019
… on insertion later.

See cdb000e commit for details.

Addressed to #1034
php-coder added a commit that referenced this issue Jun 16, 2019
@0pdd
Copy link

0pdd commented Jun 16, 2019

@php-coder 2 puzzles #1039, #1054 are still not solved.

php-coder added a commit that referenced this issue Jun 16, 2019
php-coder added a commit that referenced this issue Jun 16, 2019
@php-coder
Copy link
Owner Author

All possible duplicate key violations should be fixed now. In order to detect them, I used the following script:

$ cd docker
$ docker-compose -f docker-compose.yml -f postgres.yml down -v
$ docker-compose -f docker-compose.yml -f postgres.yml up -d
$ docker-compose -f docker-compose.yml -f postgres.yml logs -f
$ docker cp test-insert.sql mystamps_db_1:/
$ docker exec -it mystamps_db_1 psql -U mystamps -d mystamps -W -f test-insert.sql

and here is the content of test-insert.sql file:

INSERT INTO categories(name,created_at,created_by,updated_at,updated_by,slug)
VALUES('Test',now(),1,now(),1,'test');
INSERT INTO categories_aliases(category_id,name)
VALUES(1,'Foo');
INSERT INTO collections(user_id,slug,updated_at,updated_by)
VALUES(1,'test',now(),1);
INSERT INTO countries(name,created_at,created_by,updated_at,updated_by,slug)
VALUES('Test',now(),1,now(),1,'test');
INSERT INTO countries_aliases(country_id,name)
VALUES(1,'Bar');
INSERT INTO gibbons_catalog(code)
VALUES('GIB');
INSERT INTO images(type)
VALUES('PNG');
INSERT INTO images_data(content,image_id,preview)
SELECT decode('DEADBEEF', 'hex'), 1, TRUE;
INSERT INTO michel_catalog(code)
VALUES('MIC');
INSERT INTO scott_catalog(code)
VALUES('SCO');
INSERT INTO series(quantity,perforated,created_at,created_by,updated_at,updated_by,category_id)
VALUES(10,TRUE,now(),1,now(),1,1);
INSERT INTO collections_series(collection_id,series_id,number_of_stamps)
VALUES(1,1,10);
INSERT INTO series_gibbons_catalog(series_id,gibbons_id)
VALUES(1,1);
INSERT INTO series_images(series_id,image_id)
VALUES(1,1);
INSERT INTO series_import_requests(url,status_id,requested_at,requested_by,updated_at)
VALUES('http://example.com',1,now(),1,now());
INSERT INTO series_import_parsed_data(request_id,created_at,updated_at)
VALUES(1,now(),now());
INSERT INTO series_import_raw_data(request_id,page_content,created_at,updated_at)
VALUES(1,'<html>TEST</html>',now(),now());
INSERT INTO series_import_request_statuses(name)
VALUES('New status');
INSERT INTO series_michel_catalog(series_id,michel_id)
VALUES(1,1);
INSERT INTO series_sales_import_parsed_data(request_id,created_at,updated_at)
VALUES(1,now(),now());
INSERT INTO series_scott_catalog(series_id,scott_id)
VALUES(1,1);
INSERT INTO solovyov_catalog(code)
VALUES('SOL');
INSERT INTO series_solovyov_catalog(series_id,solovyov_id)
SELECT 1, id FROM solovyov_catalog WHERE code = 'SOL';
INSERT INTO yvert_catalog(code)
VALUES('YVT');
INSERT INTO series_yvert_catalog(series_id,yvert_id)
VALUES(1,1);
INSERT INTO zagorski_catalog(code)
VALUES('ZAG');
INSERT INTO series_zagorski_catalog(series_id,zagorski_id)
SELECT 1, id FROM zagorski_catalog WHERE code = 'ZAG';
INSERT INTO similar_series(series_id,similar_series_id)
VALUES(1,1);
INSERT INTO site_parsers(name)
VALUES('test parser');
INSERT INTO site_parser_params(parser_id,name,value)
VALUES(1,'foo','bar');
INSERT INTO suspicious_activities(page,type_id,ip,occurred_at)
VALUES('/foo',1,'127.0.0.1',now());
INSERT INTO suspicious_activities_types(name)
VALUES('weird');
INSERT INTO top_categories(name,created_at,created_by,updated_at,updated_by)
VALUES('parent',now(),1,now(),1);
INSERT INTO transaction_participant_groups(name,created_at,created_by,updated_at,updated_by)
VALUES('group',now(),1,now(),1);
INSERT INTO transaction_participants(name,is_buyer,is_seller)
VALUES('seller',FALSE,TRUE);
INSERT INTO series_sales(series_id,seller_id,first_price,first_currency,created_at,created_by)
VALUES(1,1,10,'RUB',now(),1);
INSERT INTO users(login,role,name,email,hash,registered_at,activated_at)
VALUES('login','USER','Name','[email protected]','XXX',now(),now());
INSERT INTO users_activation(act_key,email,created_at,lang)
VALUES('1234','[email protected]',now(),'ru');

@php-coder
Copy link
Owner Author

The application starts with PostgreSQL on test/prod profiles. The issue can be closed now.

@0pdd
Copy link

0pdd commented Oct 22, 2019

@php-coder 9 puzzles #1039, #1134, #1135, #1136, #1137, #1138, #1139, #1140, #1141 are still not solved; solved: #1054.

@0pdd
Copy link

0pdd commented Nov 30, 2019

@php-coder 8 puzzles #1039, #1134, #1135, #1136, #1137, #1138, #1139, #1141 are still not solved; solved: #1054, #1140.

php-coder added a commit that referenced this issue Aug 1, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area/infrastructure impact/changelog This change should be reflected in the NEWS.txt file kind/feature
Projects
None yet
Development

No branches or pull requests

2 participants