├── .gitignore
├── .travis.yml
├── LICENSE
├── README.md
├── pom.xml
├── repack-postgres.sh
└── src
├── license
└── LICENSE-HEADER.txt
├── main
└── java
│ └── io
│ └── airlift
│ └── testing
│ └── postgresql
│ ├── EmbeddedPostgreSql.java
│ └── TestingPostgreSqlServer.java
└── test
└── java
└── io
└── airlift
└── testing
└── postgresql
└── TestTestingPostgreSqlServer.java
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | *.ipr
3 | *.iws
4 | target/
5 | /var
6 | pom.xml.versionsBackup
7 | test-output/
8 | /atlassian-ide-plugin.xml
9 | .idea
10 | .*.swp
11 | .*.swo
12 | dist/
13 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: java
2 |
3 | jdk:
4 | - oraclejdk8
5 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 |
2 | Apache License
3 | Version 2.0, January 2004
4 | http://www.apache.org/licenses/
5 |
6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7 |
8 | 1. Definitions.
9 |
10 | "License" shall mean the terms and conditions for use, reproduction,
11 | and distribution as defined by Sections 1 through 9 of this document.
12 |
13 | "Licensor" shall mean the copyright owner or entity authorized by
14 | the copyright owner that is granting the License.
15 |
16 | "Legal Entity" shall mean the union of the acting entity and all
17 | other entities that control, are controlled by, or are under common
18 | control with that entity. For the purposes of this definition,
19 | "control" means (i) the power, direct or indirect, to cause the
20 | direction or management of such entity, whether by contract or
21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
22 | outstanding shares, or (iii) beneficial ownership of such entity.
23 |
24 | "You" (or "Your") shall mean an individual or Legal Entity
25 | exercising permissions granted by this License.
26 |
27 | "Source" form shall mean the preferred form for making modifications,
28 | including but not limited to software source code, documentation
29 | source, and configuration files.
30 |
31 | "Object" form shall mean any form resulting from mechanical
32 | transformation or translation of a Source form, including but
33 | not limited to compiled object code, generated documentation,
34 | and conversions to other media types.
35 |
36 | "Work" shall mean the work of authorship, whether in Source or
37 | Object form, made available under the License, as indicated by a
38 | copyright notice that is included in or attached to the work
39 | (an example is provided in the Appendix below).
40 |
41 | "Derivative Works" shall mean any work, whether in Source or Object
42 | form, that is based on (or derived from) the Work and for which the
43 | editorial revisions, annotations, elaborations, or other modifications
44 | represent, as a whole, an original work of authorship. For the purposes
45 | of this License, Derivative Works shall not include works that remain
46 | separable from, or merely link (or bind by name) to the interfaces of,
47 | the Work and Derivative Works thereof.
48 |
49 | "Contribution" shall mean any work of authorship, including
50 | the original version of the Work and any modifications or additions
51 | to that Work or Derivative Works thereof, that is intentionally
52 | submitted to Licensor for inclusion in the Work by the copyright owner
53 | or by an individual or Legal Entity authorized to submit on behalf of
54 | the copyright owner. For the purposes of this definition, "submitted"
55 | means any form of electronic, verbal, or written communication sent
56 | to the Licensor or its representatives, including but not limited to
57 | communication on electronic mailing lists, source code control systems,
58 | and issue tracking systems that are managed by, or on behalf of, the
59 | Licensor for the purpose of discussing and improving the Work, but
60 | excluding communication that is conspicuously marked or otherwise
61 | designated in writing by the copyright owner as "Not a Contribution."
62 |
63 | "Contributor" shall mean Licensor and any individual or Legal Entity
64 | on behalf of whom a Contribution has been received by Licensor and
65 | subsequently incorporated within the Work.
66 |
67 | 2. Grant of Copyright License. Subject to the terms and conditions of
68 | this License, each Contributor hereby grants to You a perpetual,
69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70 | copyright license to reproduce, prepare Derivative Works of,
71 | publicly display, publicly perform, sublicense, and distribute the
72 | Work and such Derivative Works in Source or Object form.
73 |
74 | 3. Grant of Patent License. Subject to the terms and conditions of
75 | this License, each Contributor hereby grants to You a perpetual,
76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77 | (except as stated in this section) patent license to make, have made,
78 | use, offer to sell, sell, import, and otherwise transfer the Work,
79 | where such license applies only to those patent claims licensable
80 | by such Contributor that are necessarily infringed by their
81 | Contribution(s) alone or by combination of their Contribution(s)
82 | with the Work to which such Contribution(s) was submitted. If You
83 | institute patent litigation against any entity (including a
84 | cross-claim or counterclaim in a lawsuit) alleging that the Work
85 | or a Contribution incorporated within the Work constitutes direct
86 | or contributory patent infringement, then any patent licenses
87 | granted to You under this License for that Work shall terminate
88 | as of the date such litigation is filed.
89 |
90 | 4. Redistribution. You may reproduce and distribute copies of the
91 | Work or Derivative Works thereof in any medium, with or without
92 | modifications, and in Source or Object form, provided that You
93 | meet the following conditions:
94 |
95 | (a) You must give any other recipients of the Work or
96 | Derivative Works a copy of this License; and
97 |
98 | (b) You must cause any modified files to carry prominent notices
99 | stating that You changed the files; and
100 |
101 | (c) You must retain, in the Source form of any Derivative Works
102 | that You distribute, all copyright, patent, trademark, and
103 | attribution notices from the Source form of the Work,
104 | excluding those notices that do not pertain to any part of
105 | the Derivative Works; and
106 |
107 | (d) If the Work includes a "NOTICE" text file as part of its
108 | distribution, then any Derivative Works that You distribute must
109 | include a readable copy of the attribution notices contained
110 | within such NOTICE file, excluding those notices that do not
111 | pertain to any part of the Derivative Works, in at least one
112 | of the following places: within a NOTICE text file distributed
113 | as part of the Derivative Works; within the Source form or
114 | documentation, if provided along with the Derivative Works; or,
115 | within a display generated by the Derivative Works, if and
116 | wherever such third-party notices normally appear. The contents
117 | of the NOTICE file are for informational purposes only and
118 | do not modify the License. You may add Your own attribution
119 | notices within Derivative Works that You distribute, alongside
120 | or as an addendum to the NOTICE text from the Work, provided
121 | that such additional attribution notices cannot be construed
122 | as modifying the License.
123 |
124 | You may add Your own copyright statement to Your modifications and
125 | may provide additional or different license terms and conditions
126 | for use, reproduction, or distribution of Your modifications, or
127 | for any such Derivative Works as a whole, provided Your use,
128 | reproduction, and distribution of the Work otherwise complies with
129 | the conditions stated in this License.
130 |
131 | 5. Submission of Contributions. Unless You explicitly state otherwise,
132 | any Contribution intentionally submitted for inclusion in the Work
133 | by You to the Licensor shall be under the terms and conditions of
134 | this License, without any additional terms or conditions.
135 | Notwithstanding the above, nothing herein shall supersede or modify
136 | the terms of any separate license agreement you may have executed
137 | with Licensor regarding such Contributions.
138 |
139 | 6. Trademarks. This License does not grant permission to use the trade
140 | names, trademarks, service marks, or product names of the Licensor,
141 | except as required for reasonable and customary use in describing the
142 | origin of the Work and reproducing the content of the NOTICE file.
143 |
144 | 7. Disclaimer of Warranty. Unless required by applicable law or
145 | agreed to in writing, Licensor provides the Work (and each
146 | Contributor provides its Contributions) on an "AS IS" BASIS,
147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148 | implied, including, without limitation, any warranties or conditions
149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150 | PARTICULAR PURPOSE. You are solely responsible for determining the
151 | appropriateness of using or redistributing the Work and assume any
152 | risks associated with Your exercise of permissions under this License.
153 |
154 | 8. Limitation of Liability. In no event and under no legal theory,
155 | whether in tort (including negligence), contract, or otherwise,
156 | unless required by applicable law (such as deliberate and grossly
157 | negligent acts) or agreed to in writing, shall any Contributor be
158 | liable to You for damages, including any direct, indirect, special,
159 | incidental, or consequential damages of any character arising as a
160 | result of this License or out of the use or inability to use the
161 | Work (including but not limited to damages for loss of goodwill,
162 | work stoppage, computer failure or malfunction, or any and all
163 | other commercial damages or losses), even if such Contributor
164 | has been advised of the possibility of such damages.
165 |
166 | 9. Accepting Warranty or Additional Liability. While redistributing
167 | the Work or Derivative Works thereof, You may choose to offer,
168 | and charge a fee for, acceptance of support, warranty, indemnity,
169 | or other liability obligations and/or rights consistent with this
170 | License. However, in accepting such obligations, You may act only
171 | on Your own behalf and on Your sole responsibility, not on behalf
172 | of any other Contributor, and only if You agree to indemnify,
173 | defend, and hold each Contributor harmless for any liability
174 | incurred by, or claims asserted against, such Contributor by reason
175 | of your accepting any such warranty or additional liability.
176 |
177 | END OF TERMS AND CONDITIONS
178 |
179 | APPENDIX: How to apply the Apache License to your work.
180 |
181 | To apply the Apache License to your work, attach the following
182 | boilerplate notice, with the fields enclosed by brackets "[]"
183 | replaced with your own identifying information. (Don't include
184 | the brackets!) The text should be enclosed in the appropriate
185 | comment syntax for the file format. We also recommend that a
186 | file or class name and description of purpose be included on the
187 | same "printed page" as the copyright notice for easier
188 | identification within third-party archives.
189 |
190 | Copyright [yyyy] [name of copyright owner]
191 |
192 | Licensed under the Apache License, Version 2.0 (the "License");
193 | you may not use this file except in compliance with the License.
194 | You may obtain a copy of the License at
195 |
196 | http://www.apache.org/licenses/LICENSE-2.0
197 |
198 | Unless required by applicable law or agreed to in writing, software
199 | distributed under the License is distributed on an "AS IS" BASIS,
200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
201 | See the License for the specific language governing permissions and
202 | limitations under the License.
203 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # This project is no longer supported
2 |
3 | We recommend using the [Testcontainers Postgres Module](https://www.testcontainers.org/modules/databases/postgres/) instead.
4 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 | 4.0.0
3 |
4 |
5 | io.airlift
6 | airbase
7 | 69
8 |
9 |
10 | testing-postgresql-server
11 | 9.6.3-4-SNAPSHOT
12 | jar
13 |
14 | testing-postgresql-server
15 | Embedded PostgreSQL server for use in tests
16 | https://github.com/airlift/testing-postgresql-server
17 |
18 | 2014
19 |
20 |
21 | scm:git:git://github.com/airlift/testing-postgresql-server.git
22 | https://github.com/airlift/testing-postgresql-server
23 | HEAD
24 |
25 |
26 |
27 | true
28 | true
29 | true
30 |
31 | 0.150
32 |
33 |
34 |
35 |
36 | org.postgresql
37 | postgresql
38 | 42.1.4
39 | runtime
40 |
41 |
42 |
43 | io.airlift
44 | command
45 | 0.3
46 |
47 |
48 |
49 | io.airlift
50 | concurrent
51 | ${dep.airlift.version}
52 |
53 |
54 |
55 | io.airlift
56 | log
57 | ${dep.airlift.version}
58 |
59 |
60 |
61 | io.airlift
62 | units
63 | 1.0
64 |
65 |
66 |
67 | com.google.guava
68 | guava
69 |
70 |
71 |
72 |
73 | org.testng
74 | testng
75 | test
76 |
77 |
78 |
79 | org.slf4j
80 | slf4j-jdk14
81 | test
82 |
83 |
84 |
85 |
86 |
87 |
88 | org.codehaus.mojo
89 | exec-maven-plugin
90 | 1.5.0
91 |
92 |
93 | generate-resources
94 |
95 | exec
96 |
97 |
98 | ${basedir}/repack-postgres.sh
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 | ${project.build.directory}/generated-resources
108 |
109 |
110 |
111 |
112 |
--------------------------------------------------------------------------------
/repack-postgres.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | set -eu
4 |
5 | # forked from https://github.com/opentable/otj-pg-embedded
6 |
7 | # https://www.enterprisedb.com/products-services-training/pgbindownload
8 |
9 | VERSION=9.6.3-1
10 | BASEURL="https://get.enterprisedb.com/postgresql"
11 |
12 | TAR=tar
13 | command -v gtar > /dev/null && TAR=gtar
14 |
15 | if ! $TAR --version | grep -q "GNU tar"
16 | then
17 | echo "GNU tar is required."
18 | echo "Hint: brew install gnu-tar"
19 | $TAR --version
20 | exit 100
21 | fi
22 |
23 | set -x
24 |
25 | cd $(dirname $0)
26 |
27 | RESOURCES=target/generated-resources
28 |
29 | mkdir -p dist $RESOURCES
30 |
31 | LINUX_NAME=postgresql-$VERSION-linux-x64-binaries.tar.gz
32 | LINUX_DIST=dist/$LINUX_NAME
33 |
34 | OSX_NAME=postgresql-$VERSION-osx-binaries.zip
35 | OSX_DIST=dist/$OSX_NAME
36 |
37 | test -e $LINUX_DIST || curl -o $LINUX_DIST "$BASEURL/$LINUX_NAME"
38 | test -e $OSX_DIST || curl -o $OSX_DIST "$BASEURL/$OSX_NAME"
39 |
40 | PACKDIR=$(mktemp -d "${TMPDIR:-/tmp}/pg.XXXXXXXXXX")
41 | tar -xzf $LINUX_DIST -C $PACKDIR
42 | pushd $PACKDIR/pgsql
43 | $TAR -czf $OLDPWD/$RESOURCES/postgresql-Linux-amd64.tar.gz \
44 | share/postgresql \
45 | lib \
46 | bin/initdb \
47 | bin/pg_ctl \
48 | bin/postgres
49 | popd
50 | rm -rf $PACKDIR
51 |
52 | PACKDIR=$(mktemp -d "${TMPDIR:-/tmp}/pg.XXXXXXXXXX")
53 | unzip -q -d $PACKDIR $OSX_DIST
54 | pushd $PACKDIR/pgsql
55 | $TAR -czf $OLDPWD/$RESOURCES/postgresql-Mac_OS_X-x86_64.tar.gz \
56 | share/postgresql \
57 | lib/libiconv.2.dylib \
58 | lib/libxml2.2.dylib \
59 | lib/libssl.1.0.0.dylib \
60 | lib/libcrypto.1.0.0.dylib \
61 | lib/libuuid.1.1.dylib \
62 | lib/postgresql/*.so \
63 | bin/initdb \
64 | bin/pg_ctl \
65 | bin/postgres
66 | popd
67 | rm -rf $PACKDIR
68 |
--------------------------------------------------------------------------------
/src/license/LICENSE-HEADER.txt:
--------------------------------------------------------------------------------
1 | Licensed under the Apache License, Version 2.0 (the "License");
2 | you may not use this file except in compliance with the License.
3 | You may obtain a copy of the License at
4 |
5 | http://www.apache.org/licenses/LICENSE-2.0
6 |
7 | Unless required by applicable law or agreed to in writing, software
8 | distributed under the License is distributed on an "AS IS" BASIS,
9 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10 | See the License for the specific language governing permissions and
11 | limitations under the License.
12 |
--------------------------------------------------------------------------------
/src/main/java/io/airlift/testing/postgresql/EmbeddedPostgreSql.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Licensed under the Apache License, Version 2.0 (the "License");
3 | * you may not use this file except in compliance with the License.
4 | * You may obtain a copy of the License at
5 | *
6 | * http://www.apache.org/licenses/LICENSE-2.0
7 | *
8 | * Unless required by applicable law or agreed to in writing, software
9 | * distributed under the License is distributed on an "AS IS" BASIS,
10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 | * See the License for the specific language governing permissions and
12 | * limitations under the License.
13 | */
14 | package io.airlift.testing.postgresql;
15 |
16 | import com.google.common.collect.ImmutableMap;
17 | import com.google.common.io.ByteStreams;
18 | import io.airlift.command.Command;
19 | import io.airlift.command.CommandFailedException;
20 | import io.airlift.log.Logger;
21 | import io.airlift.units.Duration;
22 |
23 | import java.io.Closeable;
24 | import java.io.File;
25 | import java.io.IOException;
26 | import java.io.InputStream;
27 | import java.net.ServerSocket;
28 | import java.net.Socket;
29 | import java.net.URL;
30 | import java.nio.file.Path;
31 | import java.nio.file.StandardCopyOption;
32 | import java.sql.Connection;
33 | import java.sql.DriverManager;
34 | import java.sql.ResultSet;
35 | import java.sql.SQLException;
36 | import java.sql.Statement;
37 | import java.util.List;
38 | import java.util.Map;
39 | import java.util.Map.Entry;
40 | import java.util.concurrent.ExecutorService;
41 | import java.util.concurrent.TimeUnit;
42 | import java.util.concurrent.atomic.AtomicBoolean;
43 |
44 | import static com.google.common.base.MoreObjects.toStringHelper;
45 | import static com.google.common.base.StandardSystemProperty.OS_ARCH;
46 | import static com.google.common.base.StandardSystemProperty.OS_NAME;
47 | import static com.google.common.collect.Lists.newArrayList;
48 | import static com.google.common.io.MoreFiles.deleteRecursively;
49 | import static com.google.common.io.RecursiveDeleteOption.ALLOW_INSECURE;
50 | import static io.airlift.concurrent.Threads.daemonThreadsNamed;
51 | import static java.lang.String.format;
52 | import static java.nio.file.Files.copy;
53 | import static java.nio.file.Files.createTempDirectory;
54 | import static java.util.concurrent.Executors.newCachedThreadPool;
55 |
56 | // forked from https://github.com/opentable/otj-pg-embedded
57 | final class EmbeddedPostgreSql
58 | implements Closeable
59 | {
60 | private static final Logger log = Logger.get(EmbeddedPostgreSql.class);
61 |
62 | private static final String JDBC_FORMAT = "jdbc:postgresql://localhost:%s/%s?user=%s";
63 |
64 | private static final String PG_SUPERUSER = "postgres";
65 | private static final Duration PG_STARTUP_WAIT = new Duration(10, TimeUnit.SECONDS);
66 | private static final Duration COMMAND_TIMEOUT = new Duration(30, TimeUnit.SECONDS);
67 |
68 | private final ExecutorService executor = newCachedThreadPool(daemonThreadsNamed("testing-postgresql-server-%s"));
69 | private final Path serverDirectory;
70 | private final Path dataDirectory;
71 | private final int port = randomPort();
72 | private final AtomicBoolean closed = new AtomicBoolean();
73 | private final Map postgresConfig;
74 | private final Process postmaster;
75 |
76 | public EmbeddedPostgreSql()
77 | throws IOException
78 | {
79 | serverDirectory = createTempDirectory("testing-postgresql-server");
80 | dataDirectory = serverDirectory.resolve("data");
81 |
82 | postgresConfig = ImmutableMap.builder()
83 | .put("timezone", "UTC")
84 | .put("synchronous_commit", "off")
85 | .put("max_connections", "300")
86 | .build();
87 |
88 | try {
89 | unpackPostgres(serverDirectory);
90 |
91 | pgVersion();
92 | initdb();
93 | postmaster = startPostmaster();
94 | }
95 | catch (Exception e) {
96 | close();
97 | throw e;
98 | }
99 | }
100 |
101 | public String getJdbcUrl(String userName, String dbName)
102 | {
103 | return format(JDBC_FORMAT, port, dbName, userName);
104 | }
105 |
106 | public int getPort()
107 | {
108 | return port;
109 | }
110 |
111 | public Connection getPostgresDatabase()
112 | throws SQLException
113 | {
114 | return DriverManager.getConnection(getJdbcUrl("postgres", "postgres"));
115 | }
116 |
117 | @Override
118 | public void close()
119 | {
120 | if (closed.getAndSet(true)) {
121 | return;
122 | }
123 |
124 | try {
125 | pgStop();
126 | }
127 | catch (RuntimeException e) {
128 | log.error(e, "could not stop postmaster in %s", serverDirectory);
129 | if (postmaster != null) {
130 | postmaster.destroy();
131 | }
132 | }
133 |
134 | try {
135 | deleteRecursively(serverDirectory, ALLOW_INSECURE);
136 | }
137 | catch (IOException e) {
138 | log.warn("failed to delete directory %s", serverDirectory);
139 | }
140 |
141 | executor.shutdownNow();
142 | }
143 |
144 | @Override
145 | public String toString()
146 | {
147 | return toStringHelper(this)
148 | .add("serverDirectory", serverDirectory)
149 | .add("port", port)
150 | .toString();
151 | }
152 |
153 | private static int randomPort()
154 | throws IOException
155 | {
156 | try (ServerSocket socket = new ServerSocket(0)) {
157 | return socket.getLocalPort();
158 | }
159 | }
160 |
161 | private void pgVersion()
162 | {
163 | log.info(system(pgBin("postgres"), "-V").trim());
164 | }
165 |
166 | private void initdb()
167 | {
168 | system(pgBin("initdb"),
169 | "-A", "trust",
170 | "-U", PG_SUPERUSER,
171 | "-D", dataDirectory.toString(),
172 | "-E", "UTF-8");
173 | }
174 |
175 | private Process startPostmaster()
176 | throws IOException
177 | {
178 | List args = newArrayList(
179 | pgBin("postgres"),
180 | "-D", dataDirectory.toString(),
181 | "-p", String.valueOf(port),
182 | "-i",
183 | "-F");
184 |
185 | for (Entry config : postgresConfig.entrySet()) {
186 | args.add("-c");
187 | args.add(config.getKey() + "=" + config.getValue());
188 | }
189 |
190 | Process process = new ProcessBuilder(args)
191 | .redirectErrorStream(true)
192 | .start();
193 |
194 | log.info("postmaster started on port %s. Waiting up to %s for startup to finish.", port, PG_STARTUP_WAIT);
195 |
196 | startOutputProcessor(process.getInputStream());
197 |
198 | waitForServerStartup(process);
199 |
200 | return process;
201 | }
202 |
203 | private void waitForServerStartup(Process process)
204 | throws IOException
205 | {
206 | Throwable lastCause = null;
207 | long start = System.nanoTime();
208 | while (Duration.nanosSince(start).compareTo(PG_STARTUP_WAIT) <= 0) {
209 | try {
210 | checkReady();
211 | log.debug("postmaster startup finished");
212 | return;
213 | }
214 | catch (SQLException e) {
215 | lastCause = e;
216 | log.debug("while waiting for postmaster startup", e);
217 | }
218 |
219 | try {
220 | // check if process has exited
221 | int value = process.exitValue();
222 | throw new IOException(format("postmaster exited with value %d, check stdout for more detail", value));
223 | }
224 | catch (IllegalThreadStateException ignored) {
225 | // process is still running, loop and try again
226 | }
227 |
228 | try {
229 | Thread.sleep(10);
230 | }
231 | catch (InterruptedException e) {
232 | Thread.currentThread().interrupt();
233 | return;
234 | }
235 | }
236 | throw new IOException("postmaster failed to start after " + PG_STARTUP_WAIT, lastCause);
237 | }
238 |
239 | @SuppressWarnings("EmptyTryBlock")
240 | private void checkReady()
241 | throws SQLException
242 | {
243 | // the PostgreSQL JDBC driver logs every connect failure, so check the port first
244 | try (Socket ignored = new Socket("localhost", port)) {
245 | // connect succeeded
246 | }
247 | catch (IOException e) {
248 | throw new SQLException(e);
249 | }
250 |
251 | try (Connection connection = getPostgresDatabase();
252 | Statement statement = connection.createStatement();
253 | ResultSet resultSet = statement.executeQuery("SELECT 42")) {
254 | checkSql(resultSet.next(), "no rows in result set");
255 | checkSql(resultSet.getInt(1) == 42, "wrong result");
256 | checkSql(!resultSet.next(), "multiple rows in result set");
257 | }
258 | }
259 |
260 | private static void checkSql(boolean expression, String message)
261 | throws SQLException
262 | {
263 | if (!expression) {
264 | throw new SQLException(message);
265 | }
266 | }
267 |
268 | private void pgStop()
269 | {
270 | system(pgBin("pg_ctl"),
271 | "stop",
272 | "-D", dataDirectory.toString(),
273 | "-m", "fast",
274 | "-t", "5",
275 | "-w");
276 | }
277 |
278 | private String pgBin(String binaryName)
279 | {
280 | return serverDirectory.resolve("bin").resolve(binaryName).toString();
281 | }
282 |
283 | private void startOutputProcessor(InputStream in)
284 | {
285 | executor.execute(() -> {
286 | try {
287 | ByteStreams.copy(in, System.out);
288 | }
289 | catch (IOException ignored) {
290 | }
291 | });
292 | }
293 |
294 | private String system(String... command)
295 | {
296 | try {
297 | return new Command(command)
298 | .setTimeLimit(COMMAND_TIMEOUT)
299 | .execute(executor)
300 | .getCommandOutput();
301 | }
302 | catch (CommandFailedException e) {
303 | throw new RuntimeException(e);
304 | }
305 | }
306 |
307 | private void unpackPostgres(Path target)
308 | throws IOException
309 | {
310 | String archiveName = format("/postgresql-%s.tar.gz", getPlatform());
311 | URL url = EmbeddedPostgreSql.class.getResource(archiveName);
312 | if (url == null) {
313 | throw new RuntimeException("archive not found: " + archiveName);
314 | }
315 |
316 | File archive = File.createTempFile("postgresql-", null);
317 | try {
318 | try (InputStream in = url.openStream()) {
319 | copy(in, archive.toPath(), StandardCopyOption.REPLACE_EXISTING);
320 | }
321 | system("tar", "-xzf", archive.getPath(), "-C", target.toString());
322 | }
323 | finally {
324 | if (!archive.delete()) {
325 | log.warn("failed to delete %s", archive);
326 | }
327 | }
328 | }
329 |
330 | private static String getPlatform()
331 | {
332 | return (OS_NAME.value() + "-" + OS_ARCH.value()).replace(' ', '_');
333 | }
334 | }
335 |
--------------------------------------------------------------------------------
/src/main/java/io/airlift/testing/postgresql/TestingPostgreSqlServer.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Licensed under the Apache License, Version 2.0 (the "License");
3 | * you may not use this file except in compliance with the License.
4 | * You may obtain a copy of the License at
5 | *
6 | * http://www.apache.org/licenses/LICENSE-2.0
7 | *
8 | * Unless required by applicable law or agreed to in writing, software
9 | * distributed under the License is distributed on an "AS IS" BASIS,
10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 | * See the License for the specific language governing permissions and
12 | * limitations under the License.
13 | */
14 | package io.airlift.testing.postgresql;
15 |
16 | import io.airlift.log.Logger;
17 |
18 | import java.io.Closeable;
19 | import java.io.IOException;
20 | import java.sql.Connection;
21 | import java.sql.SQLException;
22 | import java.sql.Statement;
23 |
24 | import static java.lang.String.format;
25 | import static java.util.Objects.requireNonNull;
26 |
27 | public final class TestingPostgreSqlServer
28 | implements Closeable
29 | {
30 | private static final Logger log = Logger.get(TestingPostgreSqlServer.class);
31 |
32 | private final String user;
33 | private final String database;
34 | private final int port;
35 | private final EmbeddedPostgreSql server;
36 |
37 | public TestingPostgreSqlServer(String user, String database)
38 | throws Exception
39 | {
40 | this.user = requireNonNull(user, "user is null");
41 | this.database = requireNonNull(database, "database is null");
42 |
43 | server = new EmbeddedPostgreSql();
44 | port = server.getPort();
45 |
46 | try (Connection connection = server.getPostgresDatabase()) {
47 | try (Statement statement = connection.createStatement()) {
48 | execute(statement, format("CREATE ROLE %s WITH LOGIN SUPERUSER", user));
49 | execute(statement, format("CREATE DATABASE %s OWNER %s ENCODING = 'utf8'", database, user));
50 | }
51 | }
52 | catch (Exception e) {
53 | server.close();
54 | throw e;
55 | }
56 |
57 | log.info("PostgreSQL server ready: %s", getJdbcUrl());
58 | }
59 |
60 | private static void execute(Statement statement, String sql)
61 | throws SQLException
62 | {
63 | log.debug("Executing: %s", sql);
64 | statement.execute(sql);
65 | }
66 |
67 | @Override
68 | public void close()
69 | throws IOException
70 | {
71 | server.close();
72 | }
73 |
74 | public String getUser()
75 | {
76 | return user;
77 | }
78 |
79 | public String getDatabase()
80 | {
81 | return database;
82 | }
83 |
84 | public int getPort()
85 | {
86 | return port;
87 | }
88 |
89 | public String getJdbcUrl()
90 | {
91 | return server.getJdbcUrl(user, database);
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/src/test/java/io/airlift/testing/postgresql/TestTestingPostgreSqlServer.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Licensed under the Apache License, Version 2.0 (the "License");
3 | * you may not use this file except in compliance with the License.
4 | * You may obtain a copy of the License at
5 | *
6 | * http://www.apache.org/licenses/LICENSE-2.0
7 | *
8 | * Unless required by applicable law or agreed to in writing, software
9 | * distributed under the License is distributed on an "AS IS" BASIS,
10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 | * See the License for the specific language governing permissions and
12 | * limitations under the License.
13 | */
14 | package io.airlift.testing.postgresql;
15 |
16 | import org.testng.annotations.Test;
17 |
18 | import java.net.URI;
19 | import java.sql.Connection;
20 | import java.sql.DriverManager;
21 | import java.sql.ResultSet;
22 | import java.sql.Statement;
23 |
24 | import static org.testng.Assert.assertEquals;
25 | import static org.testng.Assert.assertFalse;
26 | import static org.testng.Assert.assertTrue;
27 |
28 | public class TestTestingPostgreSqlServer
29 | {
30 | @Test
31 | public void testDatabase()
32 | throws Exception
33 | {
34 | try (TestingPostgreSqlServer server = new TestingPostgreSqlServer("testuser", "testdb")) {
35 | assertEquals(server.getUser(), "testuser");
36 | assertEquals(server.getDatabase(), "testdb");
37 | assertEquals(server.getJdbcUrl().substring(0, 5), "jdbc:");
38 | assertEquals(server.getPort(), URI.create(server.getJdbcUrl().substring(5)).getPort());
39 |
40 | try (Connection connection = DriverManager.getConnection(server.getJdbcUrl())) {
41 | try (Statement statement = connection.createStatement()) {
42 | statement.execute("CREATE TABLE test_table (c1 bigint PRIMARY KEY)");
43 | statement.execute("INSERT INTO test_table (c1) VALUES (1)");
44 | try (ResultSet resultSet = statement.executeQuery("SELECT count(*) FROM test_table")) {
45 | assertTrue(resultSet.next());
46 | assertEquals(resultSet.getLong(1), 1L);
47 | assertFalse(resultSet.next());
48 | }
49 | }
50 | }
51 | }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------