Skip to content

Commit d791972

Browse files
authored
🐛Source-postgres: added materialized views processing (#9116)
* [9012] Source-postgres: added materialized views processing
1 parent 40db22c commit d791972

File tree

7 files changed

+80
-22
lines changed

7 files changed

+80
-22
lines changed

airbyte-config/init/src/main/resources/config/STANDARD_SOURCE_DEFINITION/decd338e-5647-4c0b-adf4-da0e75f5a750.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"sourceDefinitionId": "decd338e-5647-4c0b-adf4-da0e75f5a750",
33
"name": "Postgres",
44
"dockerRepository": "airbyte/source-postgres",
5-
"dockerImageTag": "0.3.17",
5+
"dockerImageTag": "0.4.1",
66
"documentationUrl": "https://docs.airbyte.io/integrations/sources/postgres",
77
"icon": "postgresql.svg"
88
}

airbyte-config/init/src/main/resources/seed/source_definitions.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -537,7 +537,7 @@
537537
- name: Postgres
538538
sourceDefinitionId: decd338e-5647-4c0b-adf4-da0e75f5a750
539539
dockerRepository: airbyte/source-postgres
540-
dockerImageTag: 0.4.0
540+
dockerImageTag: 0.4.1
541541
documentationUrl: https://docs.airbyte.io/integrations/sources/postgres
542542
icon: postgresql.svg
543543
sourceType: database

airbyte-integrations/connectors/source-postgres-strict-encrypt/Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,5 @@ ENV APPLICATION source-postgres-strict-encrypt
1616

1717
COPY --from=build /airbyte /airbyte
1818

19-
LABEL io.airbyte.version=0.1.5
19+
LABEL io.airbyte.version=0.1.6
2020
LABEL io.airbyte.name=airbyte/source-postgres-strict-encrypt

airbyte-integrations/connectors/source-postgres/Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,5 @@ ENV APPLICATION source-postgres
1616

1717
COPY --from=build /airbyte /airbyte
1818

19-
LABEL io.airbyte.version=0.4.0
19+
LABEL io.airbyte.version=0.4.1
2020
LABEL io.airbyte.name=airbyte/source-postgres

airbyte-integrations/connectors/source-postgres/src/main/java/io/airbyte/integrations/source/postgres/PostgresSource.java

Lines changed: 64 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -117,8 +117,10 @@ public AirbyteCatalog discover(final JsonNode config) throws Exception {
117117
}
118118

119119
@Override
120-
public List<CheckedConsumer<JdbcDatabase, Exception>> getCheckOperations(final JsonNode config) throws Exception {
121-
final List<CheckedConsumer<JdbcDatabase, Exception>> checkOperations = new ArrayList<>(super.getCheckOperations(config));
120+
public List<CheckedConsumer<JdbcDatabase, Exception>> getCheckOperations(final JsonNode config)
121+
throws Exception {
122+
final List<CheckedConsumer<JdbcDatabase, Exception>> checkOperations = new ArrayList<>(
123+
super.getCheckOperations(config));
122124

123125
if (isCdc(config)) {
124126
checkOperations.add(database -> {
@@ -129,21 +131,24 @@ public List<CheckedConsumer<JdbcDatabase, Exception>> getCheckOperations(final J
129131
ps.setString(2, PostgresUtils.getPluginValue(config.get("replication_method")));
130132
ps.setString(3, config.get("database").asText());
131133

132-
LOGGER.info("Attempting to find the named replication slot using the query: " + ps.toString());
134+
LOGGER.info(
135+
"Attempting to find the named replication slot using the query: " + ps.toString());
133136

134137
return ps;
135138
}, sourceOperations::rowToJson).collect(toList());
136139

137140
if (matchingSlots.size() != 1) {
138-
throw new RuntimeException("Expected exactly one replication slot but found " + matchingSlots.size()
139-
+ ". Please read the docs and add a replication slot to your database.");
141+
throw new RuntimeException(
142+
"Expected exactly one replication slot but found " + matchingSlots.size()
143+
+ ". Please read the docs and add a replication slot to your database.");
140144
}
141145

142146
});
143147

144148
checkOperations.add(database -> {
145149
final List<JsonNode> matchingPublications = database.query(connection -> {
146-
final PreparedStatement ps = connection.prepareStatement("SELECT * FROM pg_publication WHERE pubname = ?");
150+
final PreparedStatement ps = connection
151+
.prepareStatement("SELECT * FROM pg_publication WHERE pubname = ?");
147152
ps.setString(1, config.get("replication_method").get("publication").asText());
148153

149154
LOGGER.info("Attempting to find the publication using the query: " + ps.toString());
@@ -152,8 +157,9 @@ public List<CheckedConsumer<JdbcDatabase, Exception>> getCheckOperations(final J
152157
}, sourceOperations::rowToJson).collect(toList());
153158

154159
if (matchingPublications.size() != 1) {
155-
throw new RuntimeException("Expected exactly one publication but found " + matchingPublications.size()
156-
+ ". Please read the docs and add a publication to your database.");
160+
throw new RuntimeException(
161+
"Expected exactly one publication but found " + matchingPublications.size()
162+
+ ". Please read the docs and add a publication to your database.");
157163
}
158164

159165
});
@@ -163,7 +169,9 @@ public List<CheckedConsumer<JdbcDatabase, Exception>> getCheckOperations(final J
163169
}
164170

165171
@Override
166-
public AutoCloseableIterator<AirbyteMessage> read(final JsonNode config, final ConfiguredAirbyteCatalog catalog, final JsonNode state)
172+
public AutoCloseableIterator<AirbyteMessage> read(final JsonNode config,
173+
final ConfiguredAirbyteCatalog catalog,
174+
final JsonNode state)
167175
throws Exception {
168176
// this check is used to ensure that have the pgoutput slot available so Debezium won't attempt to
169177
// create it.
@@ -177,7 +185,8 @@ public AutoCloseableIterator<AirbyteMessage> read(final JsonNode config, final C
177185
}
178186

179187
@Override
180-
public List<AutoCloseableIterator<AirbyteMessage>> getIncrementalIterators(final JdbcDatabase database,
188+
public List<AutoCloseableIterator<AirbyteMessage>> getIncrementalIterators(
189+
final JdbcDatabase database,
181190
final ConfiguredAirbyteCatalog catalog,
182191
final Map<String, TableInfo<CommonField<JDBCType>>> tableNameToTable,
183192
final StateManager stateManager,
@@ -192,10 +201,13 @@ public List<AutoCloseableIterator<AirbyteMessage>> getIncrementalIterators(final
192201
*/
193202
final JsonNode sourceConfig = database.getSourceConfig();
194203
if (isCdc(sourceConfig)) {
195-
final AirbyteDebeziumHandler handler = new AirbyteDebeziumHandler(sourceConfig, PostgresCdcTargetPosition.targetPosition(database),
204+
final AirbyteDebeziumHandler handler = new AirbyteDebeziumHandler(sourceConfig,
205+
PostgresCdcTargetPosition.targetPosition(database),
196206
PostgresCdcProperties.getDebeziumProperties(sourceConfig), catalog, false);
197-
return handler.getIncrementalIterators(new PostgresCdcSavedInfoFetcher(stateManager.getCdcStateManager().getCdcState()),
198-
new PostgresCdcStateHandler(stateManager), new PostgresCdcConnectorMetadataInjector(), emittedAt);
207+
return handler.getIncrementalIterators(
208+
new PostgresCdcSavedInfoFetcher(stateManager.getCdcStateManager().getCdcState()),
209+
new PostgresCdcStateHandler(stateManager), new PostgresCdcConnectorMetadataInjector(),
210+
emittedAt);
199211

200212
} else {
201213
return super.getIncrementalIterators(database, catalog, tableNameToTable, stateManager, emittedAt);
@@ -228,13 +240,47 @@ private static AirbyteStream removeIncrementalWithoutPk(final AirbyteStream stre
228240
}
229241

230242
@Override
231-
public Set<JdbcPrivilegeDto> getPrivilegesTableForCurrentUser(final JdbcDatabase database, final String schema) throws SQLException {
243+
public Set<JdbcPrivilegeDto> getPrivilegesTableForCurrentUser(final JdbcDatabase database,
244+
final String schema)
245+
throws SQLException {
232246
return database.query(connection -> {
233247
final PreparedStatement ps = connection.prepareStatement(
234-
"SELECT DISTINCT table_catalog, table_schema, table_name, privilege_type\n"
235-
+ "FROM information_schema.table_privileges\n"
236-
+ "WHERE grantee = ? AND privilege_type = 'SELECT'");
237-
ps.setString(1, database.getDatabaseConfig().get("username").asText());
248+
"""
249+
SELECT DISTINCT table_catalog,
250+
table_schema,
251+
table_name,
252+
privilege_type
253+
FROM information_schema.table_privileges
254+
WHERE grantee = ?
255+
AND privilege_type = 'SELECT'
256+
UNION ALL
257+
SELECT r.rolname AS table_catalog,
258+
n.nspname AS table_schema,
259+
c.relname AS table_name,
260+
-- the initial query is supposed to get a SELECT type. Since we use a UNION query
261+
-- to get Views that we can read (i.e. select) - then lets fill this columns with SELECT
262+
-- value to keep the backward-compatibility
263+
COALESCE ('SELECT') AS privilege_type
264+
FROM pg_class c
265+
JOIN pg_namespace n
266+
ON n.oid = relnamespace
267+
JOIN pg_roles r
268+
ON r.oid = relowner,
269+
Unnest(COALESCE(relacl::text[], Format('{%s=arwdDxt/%s}', rolname, rolname)::text[])) acl,
270+
Regexp_split_to_array(acl, '=|/') s
271+
WHERE r.rolname = ?
272+
AND nspname = 'public'
273+
-- 'm' means Materialized View
274+
AND c.relkind = 'm'
275+
AND (
276+
-- all grants
277+
c.relacl IS NULL
278+
-- read grant
279+
OR s[2] = 'r');
280+
""");
281+
final String username = database.getDatabaseConfig().get("username").asText();
282+
ps.setString(1, username);
283+
ps.setString(2, username);
238284
return ps;
239285
}, sourceOperations::rowToJson)
240286
.collect(toSet())

airbyte-integrations/connectors/source-postgres/src/test-integration/java/io/airbyte/integrations/io/airbyte/integration_tests/sources/PostgresSourceAcceptanceTest.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ public class PostgresSourceAcceptanceTest extends SourceAcceptanceTest {
3131

3232
private static final String STREAM_NAME = "public.id_and_name";
3333
private static final String STREAM_NAME2 = "public.starships";
34+
private static final String STREAM_NAME_MATERIALIZED_VIEW = "public.testview";
3435

3536
private PostgreSQLContainer<?> container;
3637
private JsonNode config;
@@ -67,6 +68,7 @@ protected void setupEnvironment(final TestDestinationEnv environment) throws Exc
6768
ctx.fetch("INSERT INTO id_and_name (id, name) VALUES (1,'picard'), (2, 'crusher'), (3, 'vash');");
6869
ctx.fetch("CREATE TABLE starships(id INTEGER, name VARCHAR(200));");
6970
ctx.fetch("INSERT INTO starships (id, name) VALUES (1,'enterprise-d'), (2, 'defiant'), (3, 'yamato');");
71+
ctx.fetch("CREATE MATERIALIZED VIEW testview AS select * from id_and_name where id = '2';");
7072
return null;
7173
});
7274

@@ -113,6 +115,15 @@ protected ConfiguredAirbyteCatalog getConfiguredCatalog() {
113115
STREAM_NAME2,
114116
Field.of("id", JsonSchemaPrimitive.NUMBER),
115117
Field.of("name", JsonSchemaPrimitive.STRING))
118+
.withSupportedSyncModes(Lists.newArrayList(SyncMode.FULL_REFRESH, SyncMode.INCREMENTAL))),
119+
new ConfiguredAirbyteStream()
120+
.withSyncMode(SyncMode.INCREMENTAL)
121+
.withCursorField(Lists.newArrayList("id"))
122+
.withDestinationSyncMode(DestinationSyncMode.APPEND)
123+
.withStream(CatalogHelpers.createAirbyteStream(
124+
STREAM_NAME_MATERIALIZED_VIEW,
125+
Field.of("id", JsonSchemaPrimitive.NUMBER),
126+
Field.of("name", JsonSchemaPrimitive.STRING))
116127
.withSupportedSyncModes(Lists.newArrayList(SyncMode.FULL_REFRESH, SyncMode.INCREMENTAL)))));
117128
}
118129

docs/integrations/sources/postgres.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,7 @@ According to Postgres [documentation](https://www.postgresql.org/docs/14/datatyp
257257

258258
| Version | Date | Pull Request | Subject |
259259
|:--------|:-----------|:-------------------------------------------------------|:----------------------------------------------------------------------------------------------------------------|
260+
| 0.4.1 | 2022-01-05 | [9116](https://github.com/airbytehq/airbyte/pull/9116) | Added materialized views processing |
260261
| 0.4.0 | 2021-12-13 | [8726](https://github.com/airbytehq/airbyte/pull/8726) | Support all Postgres types |
261262
| 0.3.17 | 2021-12-01 | [8371](https://github.com/airbytehq/airbyte/pull/8371) | Fixed incorrect handling "\n" in ssh key |
262263
| 0.3.16 | 2021-11-28 | [7995](https://github.com/airbytehq/airbyte/pull/7995) | Fixed money type with amount > 1000 |

0 commit comments

Comments
 (0)