-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement a simple sql template to support PostgreSQL (#49)
This commit adds the support for PostgreSQL DB Signed-off-by: Paolo Di Tommaso <[email protected]>
- Loading branch information
1 parent
b654e70
commit 8d518b7
Showing
13 changed files
with
361 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
23 changes: 23 additions & 0 deletions
23
src/main/java/io/seqera/migtool/template/DefaultSqlTemplate.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
package io.seqera.migtool.template; | ||
|
||
/** | ||
* Default SQL template for migtool SQL statements | ||
* | ||
* @author Paolo Di Tommaso <[email protected]> | ||
*/ | ||
class DefaultSqlTemplate extends SqlTemplate { | ||
@Override | ||
public String selectMaxRank(String table) { | ||
return "select max(`rank`) from " + table; | ||
} | ||
|
||
@Override | ||
public String insetMigration(String table) { | ||
return "insert into "+table+" (`rank`,`script`,`checksum`,`created_on`,`execution_time`) values (?,?,?,?,?)"; | ||
} | ||
|
||
@Override | ||
public String selectMigration(String table) { | ||
return "select `id`, `checksum`, `script` from "+table+ " where `rank` = ? and `script` = ?"; | ||
} | ||
} |
25 changes: 25 additions & 0 deletions
25
src/main/java/io/seqera/migtool/template/PostgreSqlTemplate.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
package io.seqera.migtool.template; | ||
|
||
/** | ||
* PostreSQL dialect implementation | ||
* | ||
* @author Paolo Di Tommaso <[email protected]> | ||
*/ | ||
class PostgreSqlTemplate extends SqlTemplate { | ||
|
||
@Override | ||
public String selectMaxRank(String table) { | ||
return "select max(rank) from " + table; | ||
} | ||
|
||
@Override | ||
public String insetMigration(String table) { | ||
return "insert into "+table+" (rank,script,checksum,created_on,execution_time) values (?,?,?,?,?)"; | ||
} | ||
|
||
@Override | ||
public String selectMigration(String table) { | ||
return "select id, checksum, script from "+table+ " where rank = ? and script = ?"; | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
package io.seqera.migtool.template; | ||
|
||
/** | ||
* Implements a simple template pattern to provide specialised | ||
* version of required SQL statements depending on the specified SQL "dialect" | ||
* | ||
* @author Paolo Di Tommaso <[email protected]> | ||
*/ | ||
public abstract class SqlTemplate { | ||
|
||
abstract public String selectMaxRank(String table); | ||
|
||
abstract public String insetMigration(String table); | ||
|
||
abstract public String selectMigration(String table); | ||
|
||
static public SqlTemplate from(String dialect) { | ||
if( "postgresql".equals(dialect) ) | ||
return new PostgreSqlTemplate(); | ||
else | ||
return new DefaultSqlTemplate(); | ||
} | ||
|
||
public static SqlTemplate defaultTemplate() { | ||
return new DefaultSqlTemplate(); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
create table if not exists MIGTOOL_HISTORY | ||
( | ||
id INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY, | ||
rank INTEGER NOT NULL, | ||
script VARCHAR(250) NOT NULL, | ||
checksum VARCHAR(64) NOT NULL, | ||
created_on timestamp NOT NULL, | ||
execution_time INTEGER | ||
); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
154 changes: 154 additions & 0 deletions
154
src/test/groovy/io/seqera/migtool/PostgreSqlTest.groovy
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,154 @@ | ||
package io.seqera.migtool | ||
|
||
import org.postgresql.util.PSQLException | ||
import org.testcontainers.containers.PostgreSQLContainer | ||
import spock.lang.Specification | ||
/** | ||
* | ||
* @author Paolo Di Tommaso <[email protected]> | ||
*/ | ||
class PostgreSqlTest extends Specification { | ||
|
||
private static final int PORT = 3306 | ||
|
||
|
||
static PostgreSQLContainer container | ||
|
||
static { | ||
container = new PostgreSQLContainer("postgres:16-alpine") | ||
// start it -- note: it's stopped automatically | ||
// https://www.testcontainers.org/test_framework_integration/manual_lifecycle_control/ | ||
container.start() | ||
} | ||
|
||
def 'should do something' () { | ||
given: | ||
def tool = new MigTool() | ||
.withDriver('org.postgresql.Driver') | ||
.withDialect('postgresql') | ||
.withUrl(container.getJdbcUrl()) | ||
.withUser(container.getUsername()) | ||
.withPassword(container.getPassword()) | ||
.withLocations('file:src/test/resources/migrate-db/postgresql') | ||
|
||
when: | ||
tool.run() | ||
|
||
then: | ||
tool.existTable(tool.getConnection(), 'organization') | ||
tool.existTable(tool.getConnection(), 'license') | ||
!tool.existTable(tool.getConnection(), 'foo') | ||
|
||
} | ||
|
||
def 'should run a successful Groovy script' () { | ||
given: | ||
def tool = new MigTool() | ||
.withDriver('org.postgresql.Driver') | ||
.withDialect('postgresql') | ||
.withUrl(container.getJdbcUrl()) | ||
.withUser(container.getUsername()) | ||
.withPassword(container.getPassword()) | ||
.withLocations('file:src/test/resources/migrate-db/postgresql') | ||
|
||
and: 'set up the initial tables' | ||
tool.run() | ||
|
||
when: 'run a script that inserts some data' | ||
def insertScript = ''' | ||
import java.sql.Timestamp | ||
def now = new Timestamp(System.currentTimeMillis()) | ||
def newOrgs = [ | ||
["1", "C", "C", "[email protected]", now, now], | ||
["2", "C", "C", "[email protected]", now, now], | ||
] | ||
newOrgs.each { o -> | ||
sql.executeInsert( | ||
"INSERT INTO organization(id, company, contact, email, date_created, last_updated) VALUES (?, ?, ?, ?, ?, ?)", | ||
o.toArray() | ||
) | ||
} | ||
''' | ||
def insertRecord = new MigRecord(rank: 2, script: 'V02__insert-data.groovy', checksum: 'checksum2', statements: [insertScript]) | ||
tool.runGroovyMigration(insertRecord) | ||
|
||
then: 'the script ran successfully' | ||
noExceptionThrown() | ||
|
||
when: 'run another script to check whether the data is present' | ||
def checkScript = ''' | ||
def expectedOrgIds = ["1", "2"] | ||
def orgs = sql.rows("SELECT * FROM organization") | ||
orgs.each { o -> | ||
assert o.id in expectedOrgIds | ||
} | ||
''' | ||
def checkRecord = new MigRecord(rank: 3, script: 'V03__check-data.groovy', checksum: 'checksum3', statements: [checkScript]) | ||
tool.runGroovyMigration(checkRecord) | ||
|
||
then: 'the script ran successfully (the new records are present)' | ||
noExceptionThrown() | ||
} | ||
|
||
def 'should run a failing Groovy script' () { | ||
given: | ||
def tool = new MigTool() | ||
.withDriver('org.postgresql.Driver') | ||
.withDialect('postgresql') | ||
.withUrl(container.getJdbcUrl()) | ||
.withUser(container.getUsername()) | ||
.withPassword(container.getPassword()) | ||
.withLocations('file:src/test/resources/migrate-db/postgresql') | ||
|
||
and: 'set up the initial tables' | ||
tool.run() | ||
|
||
when: 'run a script that inserts some data, but fails at some point' | ||
def insertScript = ''' | ||
import java.sql.Timestamp | ||
def now = new Timestamp(System.currentTimeMillis()) | ||
def newOrgs = [ | ||
["3", "C", "C", "[email protected]", now, now], | ||
["4", "C", "C", "[email protected]", now, now], | ||
["3", "C", "C", "[email protected]", now, now], // Duplicated id: will fail | ||
] | ||
newOrgs.each { o -> | ||
sql.executeInsert( | ||
"INSERT INTO organization(id, company, contact, email, date_created, last_updated) VALUES (?, ?, ?, ?, ?, ?)", | ||
o.toArray() | ||
) | ||
} | ||
''' | ||
def insertRecord = new MigRecord(rank: 2, script: 'V02__insert-data.groovy', checksum: 'checksum2', statements: [insertScript]) | ||
tool.runGroovyMigration(insertRecord) | ||
|
||
then: 'an exception is thrown' | ||
def e = thrown(IllegalStateException) | ||
e.message.startsWith('GROOVY MIGRATION FAILED') | ||
|
||
and: 'the root cause is present and the stack trace contains the expected offending line number' | ||
e.cause.class == PSQLException | ||
e.cause.stackTrace.any { t -> t.toString() ==~ /.+\.groovy:\d+.+/ } | ||
|
||
when: 'run another script to check whether the data is present' | ||
def checkScript = ''' | ||
def expectedMissingOrgIds = ["3", "4"] | ||
def orgs = sql.rows("SELECT * FROM organization") | ||
orgs.each { o -> | ||
assert o.id !in expectedMissingOrgIds | ||
} | ||
''' | ||
def checkRecord = new MigRecord(rank: 3, script: 'V03__check-data.groovy', checksum: 'checksum3', statements: [checkScript]) | ||
tool.runGroovyMigration(checkRecord) | ||
|
||
then: 'the script ran successfully (no records were persisted: the transaction rolled back)' | ||
noExceptionThrown() | ||
} | ||
|
||
} |
Oops, something went wrong.