Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Datastore selection for SQL / JPQL console #28

Merged
merged 5 commits into from
Aug 3, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,16 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).


## [1.4.0] - Unreleased

### Added
- Select DataStore for SQL / JPQL console in order to execute SQL / JPQL statements against different then the MAIN data store
- Diagnose Wizard now exports the SQL / JPQL result set in proper CSV format

### Dependencies
- CUBA 7.1.x


## [1.3.0] - 2019-03-30

### Dependencies
Expand Down
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,9 @@ The JPQL / SQL console allows you to interactivly interact with the database usi
![Screenshot SQL-Console](https://github.com/mariodavid/cuba-component-runtime-diagnose/blob/master/img/sql-console-screenshot.png)


![Screenshot JPQL-Console](https://github.com/mariodavid/cuba-component-runtime-diagnose/blob/master/img/jpql-console-screenshot.png)


> NOTE: for normal data diagnosis the [Entity inspector](https://doc.cuba-platform.com/manual-6.4/entity_inspector.html) is oftentimes more user friendly, even for debugging purposes.
Usage of the SQL-console is to be preferable to the entity inspector if you want to access data across tables using joins for example.
The JPQL console is useful if you want to test your JPQL queries that you want to use in your application e.g.
Expand Down
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ dependencies {
}


def hsql = 'org.hsqldb:hsqldb:2.2.9'
def hsql = 'org.hsqldb:hsqldb:2.4.1'


allprojects {
Expand Down
2 changes: 1 addition & 1 deletion config/codenarc/rulesMain.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ ruleset {
UnnecessaryBooleanInstantiation { priority = 1 }
UnnecessaryCallForLastElement { priority = 1 }
UnnecessaryCallToSubstring { priority = 1 }
UnnecessaryCast { priority = 1 }
//UnnecessaryCast { priority = 1 }
UnnecessaryCatchBlock { priority = 1 }
UnnecessaryCollectCall { priority = 1 }
UnnecessaryCollectionCall { priority = 1 }
Expand Down
Binary file modified img/diagnose-wizard-screenshot.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified img/groovy-console-screenshot.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added img/jqpl-console-screenshot.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified img/sql-console-screenshot.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package de.diedavids.cuba.runtimediagnose.db

import com.haulmont.cuba.core.Persistence
import com.haulmont.cuba.core.Query
import com.haulmont.cuba.core.global.Stores
import com.haulmont.cuba.core.global.TimeSource
import com.haulmont.cuba.core.global.UserSessionSource
import de.diedavids.cuba.runtimediagnose.diagnose.DiagnoseExecution
Expand Down Expand Up @@ -46,61 +47,60 @@ class DbDiagnoseServiceBean implements DbDiagnoseService {
@Inject
DiagnoseExecutionFactory diagnoseExecutionFactory


@Override
DbQueryResult runSqlDiagnose(String queryString, DiagnoseType diagnoseType) {
DbQueryResult runSqlDiagnose(
String queryString,
DiagnoseType diagnoseType,
String dataStore
) {
Statements queryStatements = dbQueryParser.analyseQueryString(queryString, diagnoseType)

if (!statementsAvailable(queryStatements)) {
return selectResultFactory.createFromRows([])
}

def queryStatement = queryStatements.statements[0].toString()
DiagnoseExecution diagnoseExecution = createAdHocDiagnose(queryStatement, diagnoseType)
DiagnoseExecution diagnoseExecution = createAdHocDiagnose(queryStatement, diagnoseType, dataStore)
tryToRunSqlDiagnose(diagnoseType, queryStatement, queryStatements, dataStore, diagnoseExecution)
}

private DbQueryResult tryToRunSqlDiagnose(DiagnoseType diagnoseType, String queryStatement, Statements queryStatements, String dataStore, DiagnoseExecution diagnoseExecution) {
DbQueryResult dbQueryResult
try {
dbQueryResult = getQueryResult(diagnoseType, queryStatement, queryStatements)

diagnoseExecution.handleSuccessfulExecution(getResultMessage(dbQueryResult))
dbQueryResult = getQueryResult(diagnoseType, queryStatement, queryStatements, dataStore ?: Stores.MAIN)
diagnoseExecution.handleSuccessfulExecution(dbQueryResult.resultMessage())
diagnoseExecutionLogService.logDiagnoseExecution(diagnoseExecution)
} catch (Exception e) {
dbQueryResult = selectResultFactory.createFromRows([])
diagnoseExecution.handleErrorExecution(e)
diagnoseExecutionLogService.logDiagnoseExecution(diagnoseExecution)
}

dbQueryResult
}

protected String getResultMessage(DbQueryResult dbQueryResult) {
String resultMessage = ''
if (dbQueryResult.empty) {
resultMessage = 'Execution successful'
} else {
resultMessage = dbQueryResult.entities[0].toString()
}
resultMessage
}

protected DbQueryResult getQueryResult(DiagnoseType diagnoseType, String queryStatement, Statements queryStatements) {
protected DbQueryResult getQueryResult(
DiagnoseType diagnoseType,
String queryStatement,
Statements queryStatements,
String dataStore
) {
DbQueryResult dbQueryResult
switch (diagnoseType) {
case DiagnoseType.JPQL:
dbQueryResult = executeJpqlStatement(queryStatement, queryStatements)
dbQueryResult = executeJpqlStatement(queryStatement, queryStatements, dataStore)
break
case DiagnoseType.SQL:
def sql = createSqlConnection(persistence.dataSource)
dbQueryResult = executeSqlStatement(sql, queryStatements)
dbQueryResult = executeSqlStatement(queryStatements, dataStore)
break
default:
throw new IllegalArgumentException('DiagnoseType is not supported (' + diagnoseType + ')')
}
dbQueryResult
}

protected DbQueryResult executeJpqlStatement(String queryStatement, Statements queryStatements) {
protected DbQueryResult executeJpqlStatement(String queryStatement, Statements queryStatements, String storeName) {
persistence.callInTransaction {
Query q = persistence.entityManager.createQuery(queryStatement)
Query q = persistence.getEntityManager(storeName).createQuery(queryStatement)

if (dbQueryParser.containsDataManipulation(queryStatements)) {
q.executeUpdate()
Expand All @@ -111,12 +111,13 @@ class DbDiagnoseServiceBean implements DbDiagnoseService {
}
}

protected DbQueryResult executeSqlStatement(Sql sql, Statements queryStatements) {
protected DbQueryResult executeSqlStatement(Statements queryStatements, String dataStore) {
Sql sql = createSqlConnection(persistence.getDataSource(dataStore))
dbSqlExecutor.executeStatement(sql, queryStatements.statements[0])
}

private DiagnoseExecution createAdHocDiagnose(String sqlStatement, DiagnoseType diagnoseType) {
def diagnoseExecution = diagnoseExecutionFactory.createAdHocDiagnoseExecution(sqlStatement, diagnoseType)
private DiagnoseExecution createAdHocDiagnose(String sqlStatement, DiagnoseType diagnoseType, String dataStore) {
def diagnoseExecution = diagnoseExecutionFactory.createAdHocDiagnoseExecution(sqlStatement, diagnoseType, dataStore)
setDiagnoseExecutionMetadata(diagnoseExecution)
diagnoseExecution
}
Expand All @@ -132,9 +133,8 @@ class DbDiagnoseServiceBean implements DbDiagnoseService {
setDiagnoseExecutionMetadata(diagnoseExecution)

try {
def sqlSelectResult = runSqlDiagnose(diagnoseExecution.diagnoseScript, diagnoseType)
// TODO: create CSV file with content
diagnoseExecution.handleSuccessfulExecution(sqlSelectResult.entities[0].toString())
def sqlSelectResult = runSqlDiagnose(diagnoseExecution.diagnoseScript, diagnoseType, diagnoseExecution.manifest.dataStore)
diagnoseExecution.handleSuccessfulExecution(sqlSelectResult.toCSV())
}
catch (Exception e) {
diagnoseExecution.handleErrorExecution(e)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import org.springframework.stereotype.Component
import javax.inject.Inject

@Slf4j
@Component('ddcrd_DefaultGroovyScriptTeststepBindingSupplier')
@Component('ddcrd_DefaultGroovyScriptBindingSupplier')
class DefaultGroovyScriptBindingSupplier implements GroovyScriptBindingSupplier {

@Inject
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package de.diedavids.cuba.runtimediagnose.db
import com.haulmont.cuba.core.EntityManager
import com.haulmont.cuba.core.Persistence
import com.haulmont.cuba.core.Transaction
import com.haulmont.cuba.core.global.Stores
import com.haulmont.cuba.core.global.TimeSource
import com.haulmont.cuba.core.global.UserSessionSource
import com.haulmont.cuba.security.entity.User
Expand Down Expand Up @@ -78,7 +79,7 @@ class DbDiagnoseServiceBeanSpec extends Specification {
)

dataSource = Mock(DataSource)
persistence.getDataSource() >> dataSource
persistence.getDataSource(_) >> dataSource
persistence.getTransaction() >> transaction

}
Expand All @@ -88,7 +89,7 @@ class DbDiagnoseServiceBeanSpec extends Specification {
given:
def sqlString = 'SELECT * FROM SEC_USER;'
when:
dbDiagnoseServiceBean.runSqlDiagnose(sqlString, DiagnoseType.SQL)
dbDiagnoseServiceBean.runSqlDiagnose(sqlString, DiagnoseType.SQL, Stores.MAIN)
then:
1 * dbQueryParser.analyseQueryString(sqlString, DiagnoseType.SQL)
}
Expand All @@ -98,7 +99,7 @@ class DbDiagnoseServiceBeanSpec extends Specification {
given:
def sqlString = 'select u from sec$User u'
when:
dbDiagnoseServiceBean.runSqlDiagnose(sqlString, DiagnoseType.JPQL)
dbDiagnoseServiceBean.runSqlDiagnose(sqlString, DiagnoseType.JPQL, Stores.MAIN)
then:
1 * dbQueryParser.analyseQueryString(sqlString, DiagnoseType.JPQL)
}
Expand All @@ -116,10 +117,10 @@ class DbDiagnoseServiceBeanSpec extends Specification {
dbQueryParser.analyseQueryString(_, _) >> statements

and:
diagnoseExecutionFactory.createAdHocDiagnoseExecution(_ as String, _ as DiagnoseType) >> new DiagnoseExecution()
diagnoseExecutionIsCreated()

when:
dbDiagnoseServiceBean.runSqlDiagnose(sqlString, DiagnoseType.SQL)
dbDiagnoseServiceBean.runSqlDiagnose(sqlString, DiagnoseType.SQL, Stores.MAIN)

then:
dbDiagnoseServiceBean.actualDataSource == dataSource
Expand All @@ -139,10 +140,10 @@ class DbDiagnoseServiceBeanSpec extends Specification {
dbQueryParser.analyseQueryString(_, _) >> statements

and:
diagnoseExecutionFactory.createAdHocDiagnoseExecution(_, _) >> new DiagnoseExecution()
diagnoseExecutionIsCreated()

when:
dbDiagnoseServiceBean.runSqlDiagnose(sqlString, DiagnoseType.SQL)
dbDiagnoseServiceBean.runSqlDiagnose(sqlString, DiagnoseType.SQL, Stores.MAIN)

then:
1 * dbSqlExecutor.executeStatement(_, sqlStatement) >> new DbQueryResult()
Expand All @@ -162,15 +163,19 @@ class DbDiagnoseServiceBeanSpec extends Specification {
dbQueryParser.analyseQueryString(_ as String, DiagnoseType.JPQL) >> statements

and:
diagnoseExecutionFactory.createAdHocDiagnoseExecution(_ as String, _ as DiagnoseType) >> new DiagnoseExecution()
diagnoseExecutionIsCreated()

when:
dbDiagnoseServiceBean.runSqlDiagnose(sqlString, DiagnoseType.JPQL)
dbDiagnoseServiceBean.runSqlDiagnose(sqlString, DiagnoseType.JPQL, Stores.MAIN)

then:
1 * persistence.callInTransaction(_ as Transaction.Callable)
}

private void diagnoseExecutionIsCreated() {
diagnoseExecutionFactory.createAdHocDiagnoseExecution(_ as String, _ as DiagnoseType, Stores.MAIN) >> new DiagnoseExecution()
}

def "runSqlDiagnose executes no sql script if there is no result of the sql parser"() {

given:
Expand All @@ -183,7 +188,7 @@ class DbDiagnoseServiceBeanSpec extends Specification {
dbQueryParser.analyseQueryString(_, DiagnoseType.SQL) >> statements

when:
dbDiagnoseServiceBean.runSqlDiagnose(sqlString, DiagnoseType.SQL)
dbDiagnoseServiceBean.runSqlDiagnose(sqlString, DiagnoseType.SQL, Stores.MAIN)

then:
0 * dbSqlExecutor.executeStatement(_,_)
Expand All @@ -201,7 +206,7 @@ class DbDiagnoseServiceBeanSpec extends Specification {
dbQueryParser.analyseQueryString(_, DiagnoseType.JPQL) >> statements

when:
dbDiagnoseServiceBean.runSqlDiagnose(sqlString, DiagnoseType.JPQL)
dbDiagnoseServiceBean.runSqlDiagnose(sqlString, DiagnoseType.JPQL, Stores.MAIN)

then:
0 * dbSqlExecutor.executeStatement(_,_)
Expand All @@ -219,7 +224,7 @@ class DbDiagnoseServiceBeanSpec extends Specification {
dbQueryParser.analyseQueryString(_, DiagnoseType.JPQL) >> statements

when:
dbDiagnoseServiceBean.runSqlDiagnose(sqlString, DiagnoseType.JPQL)
dbDiagnoseServiceBean.runSqlDiagnose(sqlString, DiagnoseType.JPQL, Stores.MAIN)

then:
0 * dbSqlExecutor.executeStatement(_,_)
Expand Down Expand Up @@ -257,7 +262,7 @@ class DbDiagnoseServiceBeanSpec extends Specification {
Statements statements = Mock(Statements)

when:
dbDiagnoseServiceBean.getQueryResult(diagnoseType, _ as String, statements)
dbDiagnoseServiceBean.getQueryResult(diagnoseType, _ as String, statements, Stores.MAIN)

then:
thrown(IllegalArgumentException)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ import de.diedavids.cuba.runtimediagnose.diagnose.DiagnoseType
interface DbDiagnoseService {
String NAME = 'ddcrd_SqlConsoleService'

DbQueryResult runSqlDiagnose(String queryString, DiagnoseType diagnoseType)
DbQueryResult runSqlDiagnose(String queryString, DiagnoseType diagnoseType, String dataStore)

DiagnoseExecution runSqlDiagnose(DiagnoseExecution diagnoseExecution, DiagnoseType diagnoseType)

String getSqlQuery(String jpqlQuery)
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ class DbQueryResult implements Serializable {
Collection<String> columns = []

Collection<KeyValueEntity> entities = []
private final String NEW_LINE = '\n'
private final String CSV_SEPERATOR = ','

boolean isEmpty() {
columns.empty && entities.empty
Expand All @@ -21,4 +23,36 @@ class DbQueryResult implements Serializable {
void addEntity(KeyValueEntity entity) {
entities << entity
}

String resultMessage() {
empty ? 'Execution successful' : entities[0].toString()
}

String toCSV() {
[
headerString(),
entities.collect { entity ->
entityToCSV(entity)
}
].flatten().join(NEW_LINE)
}

private String headerString() {
columns.collect { "\"$it\"" }.join(CSV_SEPERATOR)
}

String entityToCSV(KeyValueEntity entity) {

def valueColumn = columns.collect { column ->
entityColumnString(entity, column)
}

valueColumn.join(CSV_SEPERATOR)
}

private static String entityColumnString(KeyValueEntity entity, String column) {
def value = entity.getValue(column)

(value && (value != 'null')) ? "\"$value\"" : '""'
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package de.diedavids.cuba.runtimediagnose.diagnose
import groovy.transform.CompileStatic

@CompileStatic
class DiagnoseExecution implements Serializable{
class DiagnoseExecution implements Serializable {


private static final long serialVersionUID = -8288852447591914153L
Expand Down Expand Up @@ -39,19 +39,14 @@ class DiagnoseExecution implements Serializable{
}

String getExecutedScriptFileExtension() {
String extension = ''
if (manifest) {
extension = manifest.diagnoseType.name().toLowerCase()
}

extension
manifest ? manifest.diagnoseType.name().toLowerCase() : ''
}

Map<String, String> getExecutionResultFileMap() {

def executionResultFileMap = [:]
def executionResultFileMap = [:] as Map<String, String>

addResultFileIfPossible(executionResultFileMap, "diagnose.${executedScriptFileExtension}", diagnoseScript)
addResultFileIfPossible(executionResultFileMap, "diagnose.${executedScriptFileExtension}".toString(), diagnoseScript)
addResultFileIfPossible(executionResultFileMap, 'result.log', getResult(RESULT_NAME))
addResultFileIfPossible(executionResultFileMap, 'log.log', getResult(RESULT_LOG_NAME))
addResultFileIfPossible(executionResultFileMap, 'stacktrace.log', getResult(RESULT_STACKTRACE_NAME))
Expand Down
Loading