mirror of https://github.com/apache/nifi.git
NIFI-3704: Add PutDatabaseRecord processor
NIFI-3704: Incorporated review comments NIFI-3704: Updated PutDatabaseRecord documentation, including limitations on Update records NIFI-3704: Added retry paths for transient SQL errors NIFI-3704: Updated to use renamed RecordReaderFactory and catch SchemaNotFoundException NIFI-3704: Added better error handling and rollback This closes #1677. Signed-off-by: Koji Kawamura <ijokarumawak@apache.org>
This commit is contained in:
parent
ef5bac207e
commit
4303e4742a
File diff suppressed because it is too large
Load Diff
|
@ -65,8 +65,9 @@ org.apache.nifi.processors.standard.Notify
|
||||||
org.apache.nifi.processors.standard.ParseCEF
|
org.apache.nifi.processors.standard.ParseCEF
|
||||||
org.apache.nifi.processors.standard.ParseSyslog
|
org.apache.nifi.processors.standard.ParseSyslog
|
||||||
org.apache.nifi.processors.standard.PostHTTP
|
org.apache.nifi.processors.standard.PostHTTP
|
||||||
org.apache.nifi.processors.standard.PutEmail
|
org.apache.nifi.processors.standard.PutDatabaseRecord
|
||||||
org.apache.nifi.processors.standard.PutDistributedMapCache
|
org.apache.nifi.processors.standard.PutDistributedMapCache
|
||||||
|
org.apache.nifi.processors.standard.PutEmail
|
||||||
org.apache.nifi.processors.standard.PutFile
|
org.apache.nifi.processors.standard.PutFile
|
||||||
org.apache.nifi.processors.standard.PutFTP
|
org.apache.nifi.processors.standard.PutFTP
|
||||||
org.apache.nifi.processors.standard.PutJMS
|
org.apache.nifi.processors.standard.PutJMS
|
||||||
|
|
|
@ -0,0 +1,469 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.nifi.processors.standard
|
||||||
|
|
||||||
|
import org.apache.nifi.processor.exception.ProcessException
|
||||||
|
import org.apache.nifi.processors.standard.util.record.MockRecordParser
|
||||||
|
import org.apache.nifi.reporting.InitializationException
|
||||||
|
import org.apache.nifi.serialization.record.RecordField
|
||||||
|
import org.apache.nifi.serialization.record.RecordFieldType
|
||||||
|
import org.apache.nifi.serialization.record.RecordSchema
|
||||||
|
import org.apache.nifi.util.MockFlowFile
|
||||||
|
import org.apache.nifi.util.TestRunner
|
||||||
|
import org.apache.nifi.util.TestRunners
|
||||||
|
import org.apache.nifi.util.file.FileUtils
|
||||||
|
import org.junit.AfterClass
|
||||||
|
import org.junit.Before
|
||||||
|
import org.junit.BeforeClass
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
import org.junit.runners.JUnit4
|
||||||
|
|
||||||
|
import java.sql.Connection
|
||||||
|
import java.sql.DriverManager
|
||||||
|
import java.sql.ResultSet
|
||||||
|
import java.sql.SQLException
|
||||||
|
import java.sql.SQLNonTransientConnectionException
|
||||||
|
import java.sql.Statement
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals
|
||||||
|
import static org.junit.Assert.assertFalse
|
||||||
|
import static org.junit.Assert.assertNull
|
||||||
|
import static org.junit.Assert.assertTrue
|
||||||
|
import static org.junit.Assert.fail
|
||||||
|
import static org.mockito.Mockito.spy
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unit tests for the PutDatabaseRecord processor
|
||||||
|
*/
|
||||||
|
@RunWith(JUnit4.class)
|
||||||
|
class TestPutDatabaseRecord {
|
||||||
|
|
||||||
|
private static final String createPersons = "CREATE TABLE PERSONS (id integer primary key, name varchar(100), code integer)"
|
||||||
|
private final static String DB_LOCATION = "target/db_pdr"
|
||||||
|
|
||||||
|
TestRunner runner
|
||||||
|
PutDatabaseRecord processor
|
||||||
|
DBCPServiceSimpleImpl dbcp
|
||||||
|
|
||||||
|
@BeforeClass
|
||||||
|
static void setupBeforeClass() throws IOException {
|
||||||
|
System.setProperty("derby.stream.error.file", "target/derby.log")
|
||||||
|
|
||||||
|
// remove previous test database, if any
|
||||||
|
final File dbLocation = new File(DB_LOCATION)
|
||||||
|
try {
|
||||||
|
FileUtils.deleteFile(dbLocation, true)
|
||||||
|
} catch (IOException ignore) {
|
||||||
|
// Do nothing, may not have existed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterClass
|
||||||
|
static void cleanUpAfterClass() throws Exception {
|
||||||
|
try {
|
||||||
|
DriverManager.getConnection("jdbc:derby:" + DB_LOCATION + ";shutdown=true")
|
||||||
|
} catch (SQLNonTransientConnectionException ignore) {
|
||||||
|
// Do nothing, this is what happens at Derby shutdown
|
||||||
|
}
|
||||||
|
// remove previous test database, if any
|
||||||
|
final File dbLocation = new File(DB_LOCATION)
|
||||||
|
try {
|
||||||
|
FileUtils.deleteFile(dbLocation, true)
|
||||||
|
} catch (IOException ignore) {
|
||||||
|
// Do nothing, may not have existed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Before
|
||||||
|
void setUp() throws Exception {
|
||||||
|
processor = new PutDatabaseRecord()
|
||||||
|
//Mock the DBCP Controller Service so we can control the Results
|
||||||
|
dbcp = spy(new DBCPServiceSimpleImpl(DB_LOCATION))
|
||||||
|
|
||||||
|
final Map<String, String> dbcpProperties = new HashMap<>()
|
||||||
|
|
||||||
|
runner = TestRunners.newTestRunner(processor)
|
||||||
|
runner.addControllerService("dbcp", dbcp, dbcpProperties)
|
||||||
|
runner.enableControllerService(dbcp)
|
||||||
|
runner.setProperty(PutDatabaseRecord.DBCP_SERVICE, "dbcp")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testGeneratePreparedStatements() throws Exception {
|
||||||
|
|
||||||
|
final List<RecordField> fields = [new RecordField('id', RecordFieldType.INT.dataType),
|
||||||
|
new RecordField('name', RecordFieldType.STRING.dataType),
|
||||||
|
new RecordField('code', RecordFieldType.INT.dataType)]
|
||||||
|
|
||||||
|
def schema = [
|
||||||
|
getFields : {fields},
|
||||||
|
getFieldCount: {fields.size()},
|
||||||
|
getField : {int index -> fields[index]},
|
||||||
|
getDataTypes : {fields.collect {it.dataType}},
|
||||||
|
getFieldNames: {fields.collect {it.fieldName}},
|
||||||
|
getDataType : {fieldName -> fields.find {it.fieldName == fieldName}.dataType}
|
||||||
|
] as RecordSchema
|
||||||
|
|
||||||
|
def tableSchema = [
|
||||||
|
[
|
||||||
|
new PutDatabaseRecord.ColumnDescription('id', 4, true, 2),
|
||||||
|
new PutDatabaseRecord.ColumnDescription('name', 12, true, 255),
|
||||||
|
new PutDatabaseRecord.ColumnDescription('code', 4, true, 10)
|
||||||
|
],
|
||||||
|
false,
|
||||||
|
['id'] as Set<String>,
|
||||||
|
''
|
||||||
|
|
||||||
|
] as PutDatabaseRecord.TableSchema
|
||||||
|
|
||||||
|
processor.with {
|
||||||
|
try {
|
||||||
|
assertNull(generateInsert(null, null, null,
|
||||||
|
false, false, false, false,
|
||||||
|
false, false).sql)
|
||||||
|
fail('Expecting ProcessException')
|
||||||
|
} catch (ProcessException ignore) {
|
||||||
|
// Expected
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
assertNull(generateInsert(null, 'PERSONS', null,
|
||||||
|
false, false, false, false,
|
||||||
|
false, false).sql)
|
||||||
|
fail('Expecting ProcessException')
|
||||||
|
} catch (ProcessException ignore) {
|
||||||
|
// Expected
|
||||||
|
}
|
||||||
|
|
||||||
|
assertEquals('INSERT INTO PERSONS (id, name, code) VALUES (?,?,?)',
|
||||||
|
generateInsert(schema, 'PERSONS', tableSchema,
|
||||||
|
false, false, false, false,
|
||||||
|
false, false).sql)
|
||||||
|
|
||||||
|
assertEquals('DELETE FROM PERSONS WHERE id = ? AND name = ? AND code = ?',
|
||||||
|
generateDelete(schema, 'PERSONS', tableSchema,
|
||||||
|
false, false, false, false,
|
||||||
|
false, false).sql)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testInsert() throws InitializationException, ProcessException, SQLException, IOException {
|
||||||
|
recreateTable("PERSONS", createPersons)
|
||||||
|
final MockRecordParser parser = new MockRecordParser()
|
||||||
|
runner.addControllerService("parser", parser)
|
||||||
|
runner.enableControllerService(parser)
|
||||||
|
|
||||||
|
parser.addSchemaField("id", RecordFieldType.INT)
|
||||||
|
parser.addSchemaField("name", RecordFieldType.STRING)
|
||||||
|
parser.addSchemaField("code", RecordFieldType.INT)
|
||||||
|
|
||||||
|
parser.addRecord(1, 'rec1', 101)
|
||||||
|
parser.addRecord(2, 'rec2', 102)
|
||||||
|
parser.addRecord(3, 'rec3', 103)
|
||||||
|
parser.addRecord(4, 'rec4', 104)
|
||||||
|
|
||||||
|
runner.setProperty(PutDatabaseRecord.RECORD_READER_FACTORY, 'parser')
|
||||||
|
runner.setProperty(PutDatabaseRecord.STATEMENT_TYPE, PutDatabaseRecord.INSERT_TYPE)
|
||||||
|
runner.setProperty(PutDatabaseRecord.TABLE_NAME, 'PERSONS')
|
||||||
|
|
||||||
|
runner.enqueue(new byte[0])
|
||||||
|
runner.run()
|
||||||
|
|
||||||
|
runner.assertTransferCount(PutDatabaseRecord.REL_SUCCESS, 1)
|
||||||
|
final Connection conn = dbcp.getConnection()
|
||||||
|
final Statement stmt = conn.createStatement()
|
||||||
|
final ResultSet rs = stmt.executeQuery('SELECT * FROM PERSONS')
|
||||||
|
assertTrue(rs.next())
|
||||||
|
assertEquals(1, rs.getInt(1))
|
||||||
|
assertEquals('rec1', rs.getString(2))
|
||||||
|
assertEquals(101, rs.getInt(3))
|
||||||
|
assertTrue(rs.next())
|
||||||
|
assertEquals(2, rs.getInt(1))
|
||||||
|
assertEquals('rec2', rs.getString(2))
|
||||||
|
assertEquals(102, rs.getInt(3))
|
||||||
|
assertTrue(rs.next())
|
||||||
|
assertEquals(3, rs.getInt(1))
|
||||||
|
assertEquals('rec3', rs.getString(2))
|
||||||
|
assertEquals(103, rs.getInt(3))
|
||||||
|
assertTrue(rs.next())
|
||||||
|
assertEquals(4, rs.getInt(1))
|
||||||
|
assertEquals('rec4', rs.getString(2))
|
||||||
|
assertEquals(104, rs.getInt(3))
|
||||||
|
assertFalse(rs.next())
|
||||||
|
|
||||||
|
stmt.close()
|
||||||
|
conn.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testInsertNoTable() throws InitializationException, ProcessException, SQLException, IOException {
|
||||||
|
recreateTable("PERSONS", createPersons)
|
||||||
|
final MockRecordParser parser = new MockRecordParser()
|
||||||
|
runner.addControllerService("parser", parser)
|
||||||
|
runner.enableControllerService(parser)
|
||||||
|
|
||||||
|
parser.addSchemaField("id", RecordFieldType.INT)
|
||||||
|
parser.addSchemaField("name", RecordFieldType.STRING)
|
||||||
|
parser.addSchemaField("code", RecordFieldType.INT)
|
||||||
|
|
||||||
|
parser.addRecord(1, 'rec1', 101)
|
||||||
|
|
||||||
|
runner.setProperty(PutDatabaseRecord.RECORD_READER_FACTORY, 'parser')
|
||||||
|
runner.setProperty(PutDatabaseRecord.STATEMENT_TYPE, PutDatabaseRecord.INSERT_TYPE)
|
||||||
|
runner.setProperty(PutDatabaseRecord.TABLE_NAME, '${not.a.real.attr}')
|
||||||
|
|
||||||
|
runner.enqueue(new byte[0])
|
||||||
|
runner.run()
|
||||||
|
|
||||||
|
runner.assertTransferCount(PutDatabaseRecord.REL_SUCCESS, 0)
|
||||||
|
runner.assertTransferCount(PutDatabaseRecord.REL_FAILURE, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testInsertViaSqlStatementType() throws InitializationException, ProcessException, SQLException, IOException {
|
||||||
|
recreateTable("PERSONS", createPersons)
|
||||||
|
final MockRecordParser parser = new MockRecordParser()
|
||||||
|
runner.addControllerService("parser", parser)
|
||||||
|
runner.enableControllerService(parser)
|
||||||
|
|
||||||
|
parser.addSchemaField("sql", RecordFieldType.STRING)
|
||||||
|
|
||||||
|
parser.addRecord('''INSERT INTO PERSONS (id, name, code) VALUES (1, 'rec1',101)''')
|
||||||
|
parser.addRecord('''INSERT INTO PERSONS (id, name, code) VALUES (2, 'rec2',102)''')
|
||||||
|
|
||||||
|
runner.setProperty(PutDatabaseRecord.RECORD_READER_FACTORY, 'parser')
|
||||||
|
runner.setProperty(PutDatabaseRecord.STATEMENT_TYPE, PutDatabaseRecord.USE_ATTR_TYPE)
|
||||||
|
runner.setProperty(PutDatabaseRecord.TABLE_NAME, 'PERSONS')
|
||||||
|
runner.setProperty(PutDatabaseRecord.FIELD_CONTAINING_SQL, 'sql')
|
||||||
|
|
||||||
|
def attrs = [:]
|
||||||
|
attrs[PutDatabaseRecord.STATEMENT_TYPE_ATTRIBUTE] = 'sql'
|
||||||
|
runner.enqueue(new byte[0], attrs)
|
||||||
|
runner.run()
|
||||||
|
|
||||||
|
runner.assertTransferCount(PutDatabaseRecord.REL_SUCCESS, 1)
|
||||||
|
final Connection conn = dbcp.getConnection()
|
||||||
|
final Statement stmt = conn.createStatement()
|
||||||
|
final ResultSet rs = stmt.executeQuery('SELECT * FROM PERSONS')
|
||||||
|
assertTrue(rs.next())
|
||||||
|
assertEquals(1, rs.getInt(1))
|
||||||
|
assertEquals('rec1', rs.getString(2))
|
||||||
|
assertEquals(101, rs.getInt(3))
|
||||||
|
assertTrue(rs.next())
|
||||||
|
assertEquals(2, rs.getInt(1))
|
||||||
|
assertEquals('rec2', rs.getString(2))
|
||||||
|
assertEquals(102, rs.getInt(3))
|
||||||
|
assertFalse(rs.next())
|
||||||
|
|
||||||
|
stmt.close()
|
||||||
|
conn.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testSqlStatementTypeNoValue() throws InitializationException, ProcessException, SQLException, IOException {
|
||||||
|
recreateTable("PERSONS", createPersons)
|
||||||
|
final MockRecordParser parser = new MockRecordParser()
|
||||||
|
runner.addControllerService("parser", parser)
|
||||||
|
runner.enableControllerService(parser)
|
||||||
|
|
||||||
|
parser.addSchemaField("sql", RecordFieldType.STRING)
|
||||||
|
|
||||||
|
parser.addRecord('')
|
||||||
|
|
||||||
|
runner.setProperty(PutDatabaseRecord.RECORD_READER_FACTORY, 'parser')
|
||||||
|
runner.setProperty(PutDatabaseRecord.STATEMENT_TYPE, PutDatabaseRecord.USE_ATTR_TYPE)
|
||||||
|
runner.setProperty(PutDatabaseRecord.TABLE_NAME, 'PERSONS')
|
||||||
|
runner.setProperty(PutDatabaseRecord.FIELD_CONTAINING_SQL, 'sql')
|
||||||
|
|
||||||
|
def attrs = [:]
|
||||||
|
attrs[PutDatabaseRecord.STATEMENT_TYPE_ATTRIBUTE] = 'sql'
|
||||||
|
runner.enqueue(new byte[0], attrs)
|
||||||
|
runner.run()
|
||||||
|
|
||||||
|
runner.assertTransferCount(PutDatabaseRecord.REL_SUCCESS, 0)
|
||||||
|
runner.assertTransferCount(PutDatabaseRecord.REL_FAILURE, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testUpdate() throws InitializationException, ProcessException, SQLException, IOException {
|
||||||
|
recreateTable("PERSONS", createPersons)
|
||||||
|
final MockRecordParser parser = new MockRecordParser()
|
||||||
|
runner.addControllerService("parser", parser)
|
||||||
|
runner.enableControllerService(parser)
|
||||||
|
|
||||||
|
parser.addSchemaField("id", RecordFieldType.INT)
|
||||||
|
parser.addSchemaField("name", RecordFieldType.STRING)
|
||||||
|
parser.addSchemaField("code", RecordFieldType.INT)
|
||||||
|
|
||||||
|
parser.addRecord(1, 'rec1', 201)
|
||||||
|
parser.addRecord(2, 'rec2', 202)
|
||||||
|
|
||||||
|
runner.setProperty(PutDatabaseRecord.RECORD_READER_FACTORY, 'parser')
|
||||||
|
runner.setProperty(PutDatabaseRecord.STATEMENT_TYPE, PutDatabaseRecord.UPDATE_TYPE)
|
||||||
|
runner.setProperty(PutDatabaseRecord.TABLE_NAME, 'PERSONS')
|
||||||
|
|
||||||
|
// Set some existing records with different values for name and code
|
||||||
|
final Connection conn = dbcp.getConnection()
|
||||||
|
Statement stmt = conn.createStatement()
|
||||||
|
stmt.execute('''INSERT INTO PERSONS VALUES (1,'x1',101)''')
|
||||||
|
stmt.execute('''INSERT INTO PERSONS VALUES (2,'x2',102)''')
|
||||||
|
stmt.close()
|
||||||
|
|
||||||
|
runner.enqueue(new byte[0])
|
||||||
|
runner.run()
|
||||||
|
|
||||||
|
runner.assertTransferCount(PutDatabaseRecord.REL_SUCCESS, 1)
|
||||||
|
stmt = conn.createStatement()
|
||||||
|
final ResultSet rs = stmt.executeQuery('SELECT * FROM PERSONS')
|
||||||
|
assertTrue(rs.next())
|
||||||
|
assertEquals(1, rs.getInt(1))
|
||||||
|
assertEquals('rec1', rs.getString(2))
|
||||||
|
assertEquals(201, rs.getInt(3))
|
||||||
|
assertTrue(rs.next())
|
||||||
|
assertEquals(2, rs.getInt(1))
|
||||||
|
assertEquals('rec2', rs.getString(2))
|
||||||
|
assertEquals(202, rs.getInt(3))
|
||||||
|
assertFalse(rs.next())
|
||||||
|
|
||||||
|
stmt.close()
|
||||||
|
conn.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testUpdateNoPrimaryKeys() throws InitializationException, ProcessException, SQLException, IOException {
|
||||||
|
recreateTable("PERSONS", 'CREATE TABLE PERSONS (id integer, name varchar(100), code integer)')
|
||||||
|
final MockRecordParser parser = new MockRecordParser()
|
||||||
|
runner.addControllerService("parser", parser)
|
||||||
|
runner.enableControllerService(parser)
|
||||||
|
|
||||||
|
runner.setProperty(PutDatabaseRecord.RECORD_READER_FACTORY, 'parser')
|
||||||
|
runner.setProperty(PutDatabaseRecord.STATEMENT_TYPE, PutDatabaseRecord.UPDATE_TYPE)
|
||||||
|
runner.setProperty(PutDatabaseRecord.TABLE_NAME, 'PERSONS')
|
||||||
|
|
||||||
|
runner.enqueue(new byte[0])
|
||||||
|
runner.run()
|
||||||
|
|
||||||
|
runner.assertTransferCount(PutDatabaseRecord.REL_SUCCESS, 0)
|
||||||
|
runner.assertTransferCount(PutDatabaseRecord.REL_FAILURE, 1)
|
||||||
|
MockFlowFile flowFile = runner.getFlowFilesForRelationship(PutDatabaseRecord.REL_FAILURE).get(0)
|
||||||
|
assertEquals('Table \'PERSONS\' does not have a Primary Key and no Update Keys were specified', flowFile.getAttribute(PutDatabaseRecord.PUT_DATABASE_RECORD_ERROR))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testUpdateSpecifyUpdateKeys() throws InitializationException, ProcessException, SQLException, IOException {
|
||||||
|
recreateTable("PERSONS", 'CREATE TABLE PERSONS (id integer, name varchar(100), code integer)')
|
||||||
|
final MockRecordParser parser = new MockRecordParser()
|
||||||
|
runner.addControllerService("parser", parser)
|
||||||
|
runner.enableControllerService(parser)
|
||||||
|
|
||||||
|
parser.addSchemaField("id", RecordFieldType.INT)
|
||||||
|
parser.addSchemaField("name", RecordFieldType.STRING)
|
||||||
|
parser.addSchemaField("code", RecordFieldType.INT)
|
||||||
|
|
||||||
|
parser.addRecord(1, 'rec1', 201)
|
||||||
|
parser.addRecord(2, 'rec2', 202)
|
||||||
|
|
||||||
|
runner.setProperty(PutDatabaseRecord.RECORD_READER_FACTORY, 'parser')
|
||||||
|
runner.setProperty(PutDatabaseRecord.STATEMENT_TYPE, PutDatabaseRecord.UPDATE_TYPE)
|
||||||
|
runner.setProperty(PutDatabaseRecord.UPDATE_KEYS, 'id')
|
||||||
|
runner.setProperty(PutDatabaseRecord.TABLE_NAME, 'PERSONS')
|
||||||
|
|
||||||
|
// Set some existing records with different values for name and code
|
||||||
|
final Connection conn = dbcp.getConnection()
|
||||||
|
Statement stmt = conn.createStatement()
|
||||||
|
stmt.execute('''INSERT INTO PERSONS VALUES (1,'x1',101)''')
|
||||||
|
stmt.execute('''INSERT INTO PERSONS VALUES (2,'x2',102)''')
|
||||||
|
stmt.close()
|
||||||
|
|
||||||
|
runner.enqueue(new byte[0])
|
||||||
|
runner.run()
|
||||||
|
|
||||||
|
runner.assertTransferCount(PutDatabaseRecord.REL_SUCCESS, 1)
|
||||||
|
stmt = conn.createStatement()
|
||||||
|
final ResultSet rs = stmt.executeQuery('SELECT * FROM PERSONS')
|
||||||
|
assertTrue(rs.next())
|
||||||
|
assertEquals(1, rs.getInt(1))
|
||||||
|
assertEquals('rec1', rs.getString(2))
|
||||||
|
assertEquals(201, rs.getInt(3))
|
||||||
|
assertTrue(rs.next())
|
||||||
|
assertEquals(2, rs.getInt(1))
|
||||||
|
assertEquals('rec2', rs.getString(2))
|
||||||
|
assertEquals(202, rs.getInt(3))
|
||||||
|
assertFalse(rs.next())
|
||||||
|
|
||||||
|
stmt.close()
|
||||||
|
conn.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testDelete() throws InitializationException, ProcessException, SQLException, IOException {
|
||||||
|
recreateTable("PERSONS", createPersons)
|
||||||
|
Connection conn = dbcp.getConnection()
|
||||||
|
Statement stmt = conn.createStatement()
|
||||||
|
stmt.execute("INSERT INTO PERSONS VALUES (1,'rec1', 101)")
|
||||||
|
stmt.execute("INSERT INTO PERSONS VALUES (2,'rec2', 102)")
|
||||||
|
stmt.execute("INSERT INTO PERSONS VALUES (3,'rec3', 103)")
|
||||||
|
stmt.close()
|
||||||
|
|
||||||
|
final MockRecordParser parser = new MockRecordParser()
|
||||||
|
runner.addControllerService("parser", parser)
|
||||||
|
runner.enableControllerService(parser)
|
||||||
|
|
||||||
|
parser.addSchemaField("id", RecordFieldType.INT)
|
||||||
|
parser.addSchemaField("name", RecordFieldType.STRING)
|
||||||
|
parser.addSchemaField("code", RecordFieldType.INT)
|
||||||
|
|
||||||
|
parser.addRecord(2, 'rec2', 102)
|
||||||
|
|
||||||
|
runner.setProperty(PutDatabaseRecord.RECORD_READER_FACTORY, 'parser')
|
||||||
|
runner.setProperty(PutDatabaseRecord.STATEMENT_TYPE, PutDatabaseRecord.DELETE_TYPE)
|
||||||
|
runner.setProperty(PutDatabaseRecord.TABLE_NAME, 'PERSONS')
|
||||||
|
|
||||||
|
runner.enqueue(new byte[0])
|
||||||
|
runner.run()
|
||||||
|
|
||||||
|
runner.assertTransferCount(PutDatabaseRecord.REL_SUCCESS, 1)
|
||||||
|
stmt = conn.createStatement()
|
||||||
|
final ResultSet rs = stmt.executeQuery('SELECT * FROM PERSONS')
|
||||||
|
assertTrue(rs.next())
|
||||||
|
assertEquals(1, rs.getInt(1))
|
||||||
|
assertEquals('rec1', rs.getString(2))
|
||||||
|
assertEquals(101, rs.getInt(3))
|
||||||
|
assertTrue(rs.next())
|
||||||
|
assertEquals(3, rs.getInt(1))
|
||||||
|
assertEquals('rec3', rs.getString(2))
|
||||||
|
assertEquals(103, rs.getInt(3))
|
||||||
|
assertFalse(rs.next())
|
||||||
|
|
||||||
|
stmt.close()
|
||||||
|
conn.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
private void recreateTable(String tableName, String createSQL) throws ProcessException, SQLException {
|
||||||
|
final Connection conn = dbcp.getConnection()
|
||||||
|
final Statement stmt = conn.createStatement()
|
||||||
|
try {
|
||||||
|
stmt.executeUpdate("drop table " + tableName)
|
||||||
|
} catch (SQLException ignore) {
|
||||||
|
// Do nothing, may not have existed
|
||||||
|
}
|
||||||
|
stmt.executeUpdate(createSQL)
|
||||||
|
stmt.close()
|
||||||
|
conn.close()
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,51 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.nifi.processors.standard;
|
||||||
|
|
||||||
|
import org.apache.nifi.controller.AbstractControllerService;
|
||||||
|
import org.apache.nifi.dbcp.DBCPService;
|
||||||
|
import org.apache.nifi.processor.exception.ProcessException;
|
||||||
|
|
||||||
|
import java.sql.Connection;
|
||||||
|
import java.sql.DriverManager;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simple implementation only for GenerateTableFetch processor testing.
|
||||||
|
*/
|
||||||
|
public class DBCPServiceSimpleImpl extends AbstractControllerService implements DBCPService {
|
||||||
|
|
||||||
|
private String databaseLocation;
|
||||||
|
|
||||||
|
public DBCPServiceSimpleImpl(final String databaseLocation) {
|
||||||
|
this.databaseLocation = databaseLocation;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getIdentifier() {
|
||||||
|
return "dbcp";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Connection getConnection() throws ProcessException {
|
||||||
|
try {
|
||||||
|
Class.forName("org.apache.derby.jdbc.EmbeddedDriver");
|
||||||
|
return DriverManager.getConnection("jdbc:derby:" + databaseLocation + ";create=true");
|
||||||
|
} catch (final Exception e) {
|
||||||
|
throw new ProcessException("getConnection failed: " + e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue