-
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.
Merge pull request #1 from creditdatamw/feature/sql-export
Adds export to SQL INSERT statements functionality
- Loading branch information
Showing
10 changed files
with
348 additions
and
115 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
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,4 +3,6 @@ | |
/.idea/ | ||
|
||
# Ignore Gradle build output directory | ||
build | ||
build | ||
*.xlsx | ||
/*.sql |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package zefaker | ||
|
||
class ColumnDef { | ||
int index | ||
String name | ||
Closure faker | ||
|
||
public ColumnDef(int index, String name, Closure faker) { | ||
this.index = index | ||
this.name = name | ||
this.faker = faker | ||
} | ||
} |
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,8 @@ | ||
package zefaker | ||
|
||
enum ColumnQuotes { | ||
NONE, | ||
MSSQL, | ||
MYSQL, | ||
POSTGRESQL | ||
} |
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,105 @@ | ||
package zefaker | ||
|
||
import com.github.javafaker.Faker | ||
import org.apache.poi.ss.usermodel.Cell | ||
import org.apache.poi.ss.usermodel.Row | ||
import org.apache.poi.ss.usermodel.Sheet | ||
import org.apache.poi.ss.usermodel.Workbook | ||
import org.apache.poi.ss.util.WorkbookUtil | ||
import org.apache.poi.xssf.usermodel.XSSFWorkbook | ||
import org.apache.poi.xssf.streaming.SXSSFWorkbook; | ||
|
||
import java.nio.file.Paths | ||
import java.nio.file.Files | ||
import java.util.stream.Collectors | ||
import java.util.concurrent.CountDownLatch | ||
import java.util.concurrent.atomic.AtomicLong | ||
|
||
class ExcelFileGenerator implements Runnable { | ||
Workbook wb | ||
def columnDefs | ||
def filePath | ||
def maxRows | ||
def sheetName = "Sheet 1" | ||
final CountDownLatch latch | ||
final Faker faker | ||
final AtomicLong generated = new AtomicLong(0) | ||
|
||
ExcelFileGenerator(faker, filePath, columnDefs, sheetName, streamingBatchSize, maxRows, latch) { | ||
this.faker = faker | ||
this.filePath = filePath | ||
this.sheetName = sheetName | ||
this.wb = new SXSSFWorkbook(streamingBatchSize) | ||
this.columnDefs = columnDefs | ||
this.latch = latch | ||
this.maxRows = maxRows | ||
} | ||
|
||
void run() { | ||
|
||
try { | ||
def fos = Files.newOutputStream(filePath) | ||
def sheet = this.wb.createSheet(WorkbookUtil.createSafeSheetName(sheetName)) | ||
def dateCellStyle = this.wb.createCellStyle() | ||
|
||
dateCellStyle.setDataFormat( | ||
// TODO: Enable user to specify a date format in the script | ||
this.wb.getCreationHelper().createDataFormat().getFormat("yyyy-mm-dd") | ||
); | ||
|
||
// Create file headers | ||
def row = sheet.createRow(0) | ||
int i = 0 | ||
|
||
columnDefs.keySet().each { | ||
def cell = row.createCell(it.index) | ||
cell.setCellValue(it.name) | ||
// TODO: if(s.contains("DATE")) cell.setCellStyle(dateCellStyle); | ||
++i; | ||
} | ||
|
||
try { | ||
populateSheet(sheet, columnDefs) | ||
} catch(Exception e) { | ||
System.err.println("ERROR: Exception during file processing: " + e.getMessage()) | ||
} finally { | ||
wb.write(fos) | ||
fos.close() | ||
|
||
wb.dispose() // remove temporary files | ||
wb.close() | ||
sheet = null | ||
} | ||
|
||
latch.countDown() | ||
|
||
} catch (IOException e) { | ||
latch.countDown() | ||
throw new RuntimeException("Failed to generate file", e) | ||
} | ||
} | ||
|
||
/** | ||
* Populate the Sheet using the given column definitions | ||
* @param sheet The sheet to write to | ||
* @param columnDefs map of column definitions | ||
*/ | ||
void populateSheet(sheet, columnDefs) { | ||
int nextRow = 1 | ||
Row row = null | ||
|
||
while(generated.get() < maxRows) { | ||
row = sheet.createRow(nextRow) | ||
|
||
columnDefs.each { | ||
def col = it.getKey() | ||
def fakerFunc = it.getValue() | ||
def generatedValue = fakerFunc(faker) | ||
row.createCell(col.index).setCellValue(generatedValue) | ||
} | ||
|
||
nextRow++; | ||
generated.incrementAndGet(); | ||
} | ||
} | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
package zefaker | ||
|
||
import com.github.javafaker.Faker | ||
|
||
import java.nio.file.Paths | ||
import java.nio.file.Files | ||
import java.io.BufferedWriter | ||
import java.util.stream.Collectors | ||
import java.util.concurrent.CountDownLatch | ||
import java.util.concurrent.atomic.AtomicLong | ||
|
||
class SqlFileGenerator implements Runnable { | ||
def columnDefs | ||
def filePath | ||
def tableName = "data" | ||
def maxRows = 10 | ||
def quoteMode = ColumnQuotes.NONE | ||
final CountDownLatch latch | ||
final Faker faker | ||
final AtomicLong generated = new AtomicLong(0) | ||
|
||
final VALUES_PLACEHOLDER = "__values__" | ||
|
||
SqlFileGenerator(faker, filePath, columnDefs, tableName, maxRows, latch) { | ||
this.faker = faker | ||
this.filePath = filePath | ||
this.columnDefs = columnDefs | ||
this.tableName = tableName | ||
this.latch = latch | ||
this.maxRows = maxRows | ||
} | ||
|
||
void setQuoteMode(quoteMode) { | ||
this.quoteMode = quoteMode | ||
} | ||
|
||
void run() { | ||
StringBuilder sb = new StringBuilder() | ||
sb.append("INSERT INTO ") | ||
.append(tableName) | ||
.append(" (") | ||
// TODO: consider order of the columns? | ||
.append(columnDefs.keySet() | ||
.stream() | ||
.map({ it -> | ||
switch(quoteMode) { | ||
case ColumnQuotes.MSSQL: | ||
return String.format("[%s]", it.name) | ||
case ColumnQuotes.MYSQL: | ||
return String.format("`%s`", it.name) | ||
case ColumnQuotes.POSTGRESQL: | ||
return String.format("\"%s\"", it.name) | ||
case ColumnQuotes.NONE: | ||
default: | ||
return it.name | ||
} | ||
}) | ||
.collect(Collectors.joining(","))) | ||
.append(") ") | ||
.append("VALUES (") | ||
.append(VALUES_PLACEHOLDER) | ||
.append(");"); | ||
|
||
def sqlInsertTemplate = sb.toString() | ||
|
||
def bufferedWriter = Files.newBufferedWriter(filePath) | ||
|
||
try { | ||
def rowValues = new Object[columnDefs.size()] | ||
|
||
while(generated.get() < maxRows) { | ||
columnDefs.each { | ||
def col = it.getKey() | ||
def fakerFunc = it.getValue() | ||
def generatedValue = fakerFunc(faker) | ||
rowValues[col.index] = String.valueOf(generatedValue) | ||
} | ||
|
||
def sqlStatement = createInsertStatement(sqlInsertTemplate, rowValues) | ||
bufferedWriter.write(sqlStatement) | ||
bufferedWriter.newLine() | ||
|
||
generated.incrementAndGet(); | ||
} | ||
|
||
bufferedWriter.flush(); | ||
|
||
} catch (Exception e) { | ||
bufferedWriter.close() | ||
|
||
throw new RuntimeException("Failed to generate file", e) | ||
} finally { | ||
latch.countDown() | ||
} | ||
} | ||
|
||
String createInsertStatement(sqlTemplate, rowValues) { | ||
//def rowValuesQuotesReplaced = rowValues.map { | ||
// | ||
// return it | ||
//} | ||
// use rowValuesQuotesReplaced | ||
def valuesString = Arrays.stream(rowValues) | ||
.map({ it -> | ||
if (it == null) return it | ||
if (it instanceof String) { | ||
return String.format("'%s'", it.replace("'", "\\'")) | ||
} | ||
return String.format("'%s'", it) | ||
}) | ||
.collect(Collectors.joining(",")) | ||
|
||
return sqlTemplate.replace(VALUES_PLACEHOLDER, valuesString) | ||
} | ||
} |
Oops, something went wrong.