@@ -14,6 +14,7 @@ import io.airbyte.cdk.db.JdbcCompatibleSourceOperations
14
14
import io.airbyte.cdk.db.SqlDatabase
15
15
import io.airbyte.cdk.db.factory.DataSourceFactory.close
16
16
import io.airbyte.cdk.db.factory.DataSourceFactory.create
17
+ import io.airbyte.cdk.db.jdbc.AirbyteRecordData
17
18
import io.airbyte.cdk.db.jdbc.JdbcConstants.INTERNAL_COLUMN_NAME
18
19
import io.airbyte.cdk.db.jdbc.JdbcConstants.INTERNAL_COLUMN_SIZE
19
20
import io.airbyte.cdk.db.jdbc.JdbcConstants.INTERNAL_COLUMN_TYPE
@@ -42,6 +43,7 @@ import io.airbyte.cdk.integrations.source.jdbc.dto.JdbcPrivilegeDto
42
43
import io.airbyte.cdk.integrations.source.relationaldb.AbstractDbSource
43
44
import io.airbyte.cdk.integrations.source.relationaldb.CursorInfo
44
45
import io.airbyte.cdk.integrations.source.relationaldb.RelationalDbQueryUtils
46
+ import io.airbyte.cdk.integrations.source.relationaldb.RelationalDbQueryUtils.enquoteIdentifier
45
47
import io.airbyte.cdk.integrations.source.relationaldb.TableInfo
46
48
import io.airbyte.cdk.integrations.source.relationaldb.state.StateManager
47
49
import io.airbyte.commons.functional.CheckedConsumer
@@ -100,50 +102,73 @@ abstract class AbstractJdbcSource<Datatype>(
100
102
tableName : String ,
101
103
syncMode : SyncMode ,
102
104
cursorField : Optional <String >
103
- ): AutoCloseableIterator <JsonNode >? {
104
- LOGGER .info(" Queueing query for table: {}" , tableName)
105
- val quoteString = this .quoteString!!
106
- // This corresponds to the initial sync for in INCREMENTAL_MODE, where the ordering of the
107
- // records
108
- // matters
109
- // as intermediate state messages are emitted (if the connector emits intermediate state).
110
- if (syncMode == SyncMode .INCREMENTAL && stateEmissionFrequency > 0 ) {
111
- val quotedCursorField =
112
- RelationalDbQueryUtils .enquoteIdentifier(cursorField.get(), quoteString)
113
- return RelationalDbQueryUtils .queryTable(
114
- database,
115
- String .format(
116
- " SELECT %s FROM %s ORDER BY %s ASC" ,
117
- RelationalDbQueryUtils .enquoteIdentifierList(columnNames, quoteString),
118
- RelationalDbQueryUtils .getFullyQualifiedTableNameWithQuoting(
119
- schemaName,
120
- tableName,
121
- quoteString
122
- ),
123
- quotedCursorField
124
- ),
125
- tableName,
126
- schemaName
127
- )
128
- } else {
129
- // If we are in FULL_REFRESH mode, state messages are never emitted, so we don't care
130
- // about ordering
131
- // of the records.
132
- return RelationalDbQueryUtils .queryTable(
133
- database,
134
- String .format(
135
- " SELECT %s FROM %s" ,
136
- RelationalDbQueryUtils .enquoteIdentifierList(columnNames, quoteString),
137
- RelationalDbQueryUtils .getFullyQualifiedTableNameWithQuoting(
138
- schemaName,
139
- tableName,
140
- quoteString
105
+ ): AutoCloseableIterator <AirbyteRecordData > {
106
+ AbstractDbSource .LOGGER .info(" Queueing query for table: {}" , tableName)
107
+ val airbyteStream = AirbyteStreamUtils .convertFromNameAndNamespace(tableName, schemaName)
108
+ return AutoCloseableIterators .lazyIterator<AirbyteRecordData >(
109
+ Supplier <AutoCloseableIterator <AirbyteRecordData >> {
110
+ try {
111
+ val stream =
112
+ database.unsafeQuery(
113
+ { connection: Connection ->
114
+ AbstractDbSource .LOGGER .info(
115
+ " Preparing query for table: {}" ,
116
+ tableName
117
+ )
118
+ val fullTableName: String =
119
+ RelationalDbQueryUtils .getFullyQualifiedTableNameWithQuoting(
120
+ schemaName,
121
+ tableName,
122
+ quoteString!!
123
+ )
124
+
125
+ val wrappedColumnNames =
126
+ getWrappedColumnNames(
127
+ database,
128
+ connection,
129
+ columnNames,
130
+ schemaName,
131
+ tableName
132
+ )
133
+ val sql =
134
+ java.lang.StringBuilder (
135
+ String .format(
136
+ " SELECT %s FROM %s" ,
137
+ wrappedColumnNames,
138
+ fullTableName
139
+ )
140
+ )
141
+ // if the connector emits intermediate states, the incremental query
142
+ // must be sorted by the cursor
143
+ // field
144
+ if (
145
+ syncMode == SyncMode .INCREMENTAL && stateEmissionFrequency > 0
146
+ ) {
147
+ val quotedCursorField: String =
148
+ enquoteIdentifier(cursorField.get(), quoteString)
149
+ sql.append(String .format(" ORDER BY %s ASC" , quotedCursorField))
150
+ }
151
+
152
+ val preparedStatement = connection.prepareStatement(sql.toString())
153
+ AbstractDbSource .LOGGER .info(
154
+ " Executing query for table {}: {}" ,
155
+ tableName,
156
+ preparedStatement
157
+ )
158
+ preparedStatement
159
+ },
160
+ sourceOperations::convertDatabaseRowToAirbyteRecordData
161
+ )
162
+ return @Supplier AutoCloseableIterators .fromStream<AirbyteRecordData >(
163
+ stream,
164
+ airbyteStream
141
165
)
142
- ),
143
- tableName,
144
- schemaName
145
- )
146
- }
166
+ } catch (e: SQLException ) {
167
+ throw java.lang.RuntimeException (e)
168
+ }
169
+ },
170
+ airbyteStream
171
+ )
147
172
}
148
173
149
174
/* *
@@ -433,37 +458,34 @@ abstract class AbstractJdbcSource<Datatype>(
433
458
return sourceOperations.isCursorType(type)
434
459
}
435
460
436
- public override fun queryTableIncremental (
461
+ override fun queryTableIncremental (
437
462
database : JdbcDatabase ,
438
463
columnNames : List <String >,
439
464
schemaName : String? ,
440
465
tableName : String ,
441
466
cursorInfo : CursorInfo ,
442
467
cursorFieldType : Datatype
443
- ): AutoCloseableIterator <JsonNode > ? {
444
- LOGGER .info(" Queueing query for table: {}" , tableName)
468
+ ): AutoCloseableIterator <AirbyteRecordData > {
469
+ AbstractDbSource . LOGGER .info(" Queueing query for table: {}" , tableName)
445
470
val airbyteStream = AirbyteStreamUtils .convertFromNameAndNamespace(tableName, schemaName)
446
471
return AutoCloseableIterators .lazyIterator(
447
472
{
448
- val quoteString = this .quoteString!!
449
473
try {
450
474
val stream =
451
475
database.unsafeQuery(
452
- CheckedFunction <Connection , PreparedStatement , SQLException ?> {
453
- connection: Connection ->
454
- LOGGER .info(" Preparing query for table: {}" , tableName)
455
- val fullTableName =
476
+ { connection: Connection ->
477
+ AbstractDbSource .LOGGER .info(
478
+ " Preparing query for table: {}" ,
479
+ tableName
480
+ )
481
+ val fullTableName: String =
456
482
RelationalDbQueryUtils .getFullyQualifiedTableNameWithQuoting(
457
483
schemaName,
458
484
tableName,
459
- quoteString
460
- )
461
- val quotedCursorField =
462
- RelationalDbQueryUtils .enquoteIdentifier(
463
- cursorInfo.cursorField,
464
- quoteString
485
+ quoteString!!
465
486
)
466
-
487
+ val quotedCursorField: String =
488
+ enquoteIdentifier(cursorInfo.cursorField, quoteString)
467
489
val operator : String
468
490
if (cursorInfo.cursorRecordCount <= 0L ) {
469
491
operator = " >"
@@ -476,7 +498,7 @@ abstract class AbstractJdbcSource<Datatype>(
476
498
cursorFieldType,
477
499
cursorInfo.cursor
478
500
)
479
- LOGGER .info(
501
+ AbstractDbSource . LOGGER .info(
480
502
" Table {} cursor count: expected {}, actual {}" ,
481
503
tableName,
482
504
cursorInfo.cursorRecordCount,
@@ -489,7 +511,6 @@ abstract class AbstractJdbcSource<Datatype>(
489
511
" >="
490
512
}
491
513
}
492
-
493
514
val wrappedColumnNames =
494
515
getWrappedColumnNames(
495
516
database,
@@ -514,9 +535,8 @@ abstract class AbstractJdbcSource<Datatype>(
514
535
if (stateEmissionFrequency > 0 ) {
515
536
sql.append(String .format(" ORDER BY %s ASC" , quotedCursorField))
516
537
}
517
-
518
538
val preparedStatement = connection.prepareStatement(sql.toString())
519
- LOGGER .info(
539
+ AbstractDbSource . LOGGER .info(
520
540
" Executing query for table {}: {}" ,
521
541
tableName,
522
542
preparedStatement
@@ -529,12 +549,9 @@ abstract class AbstractJdbcSource<Datatype>(
529
549
)
530
550
preparedStatement
531
551
},
532
- CheckedFunction <ResultSet , JsonNode , SQLException ?> {
533
- queryResult: ResultSet ? ->
534
- sourceOperations.rowToJson(queryResult!! )
535
- }
552
+ sourceOperations::convertDatabaseRowToAirbyteRecordData
536
553
)
537
- return @lazyIterator AutoCloseableIterators .fromStream<JsonNode >(
554
+ return @lazyIterator AutoCloseableIterators .fromStream<AirbyteRecordData >(
538
555
stream,
539
556
airbyteStream
540
557
)
@@ -546,6 +563,10 @@ abstract class AbstractJdbcSource<Datatype>(
546
563
)
547
564
}
548
565
566
+ protected fun getCountColumnName (): String {
567
+ return " record_count"
568
+ }
569
+
549
570
/* * Some databases need special column names in the query. */
550
571
@Throws(SQLException ::class )
551
572
protected fun getWrappedColumnNames (
@@ -558,9 +579,6 @@ abstract class AbstractJdbcSource<Datatype>(
558
579
return RelationalDbQueryUtils .enquoteIdentifierList(columnNames, quoteString!! )
559
580
}
560
581
561
- protected val countColumnName: String
562
- get() = " record_count"
563
-
564
582
@Throws(SQLException ::class )
565
583
protected fun getActualCursorRecordCount (
566
584
connection : Connection ,
@@ -569,7 +587,7 @@ abstract class AbstractJdbcSource<Datatype>(
569
587
cursorFieldType : Datatype ,
570
588
cursor : String?
571
589
): Long {
572
- val columnName = countColumnName
590
+ val columnName = getCountColumnName()
573
591
val cursorRecordStatement: PreparedStatement
574
592
if (cursor == null ) {
575
593
val cursorRecordQuery =
0 commit comments