Skip to content

Commit 3caf6fd

Browse files
committed
Create SmtTable class to handle name conversion
1 parent 47e2f90 commit 3caf6fd

File tree

2 files changed

+94
-70
lines changed

2 files changed

+94
-70
lines changed

core/src/main/kotlin/org/evomaster/core/solver/SmtLibGenerator.kt

Lines changed: 64 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import org.evomaster.client.java.controller.api.dto.database.schema.DbInfoDto
1111
import org.evomaster.client.java.controller.api.dto.database.schema.ForeignKeyDto
1212
import org.evomaster.client.java.controller.api.dto.database.schema.TableDto
1313
import org.evomaster.core.logging.LoggingUtil
14-
import org.evomaster.core.utils.StringUtils
1514
import org.evomaster.dbconstraint.ConstraintDatabaseType
1615
import org.evomaster.dbconstraint.ast.SqlCondition
1716
import net.sf.jsqlparser.JSQLParserException
@@ -31,6 +30,9 @@ class SmtLibGenerator(private val schema: DbInfoDto, private val numberOfRows: I
3130

3231
private var parser = JSqlConditionParser()
3332

33+
private val smtTables: List<SmtTable> = schema.tables.map { SmtTable(it) }
34+
private val smtTableByOriginalName: Map<String, SmtTable> = smtTables.associateBy { it.originalName }
35+
3436
/**
3537
* Main method to generate SMT-LIB representation from SQL query.
3638
*
@@ -57,18 +59,15 @@ class SmtLibGenerator(private val schema: DbInfoDto, private val numberOfRows: I
5759
* @param smt The SMT-LIB object to which table definitions are added.
5860
*/
5961
private fun appendTableDefinitions(smt: SMTLib) {
60-
for (table in schema.tables) {
61-
val smtTableName = convertToAscii(table.id.name)
62-
val dataTypeName = "${StringUtils.capitalization(smtTableName)}Row"
63-
62+
for (smtTable in smtTables) {
6463
// Declare datatype for the table
6564
smt.addNode(
66-
DeclareDatatypeSMTNode(dataTypeName, getConstructors(table))
65+
DeclareDatatypeSMTNode(smtTable.dataTypeName, getConstructors(smtTable))
6766
)
6867

6968
// Declare constants for each row
7069
for (i in 1..numberOfRows) {
71-
smt.addNode(DeclareConstSMTNode("${smtTableName.lowercase()}$i", dataTypeName))
70+
smt.addNode(DeclareConstSMTNode("${smtTable.smtName}$i", smtTable.dataTypeName))
7271
}
7372
}
7473
}
@@ -79,23 +78,22 @@ class SmtLibGenerator(private val schema: DbInfoDto, private val numberOfRows: I
7978
* @param smt The SMT-LIB object to which table constraints are added.
8079
*/
8180
private fun appendTableConstraints(smt: SMTLib) {
82-
for (table in schema.tables) {
83-
appendUniqueConstraints(smt, table)
84-
appendCheckConstraints(smt, table)
81+
for (smtTable in smtTables) {
82+
appendUniqueConstraints(smt, smtTable)
83+
appendCheckConstraints(smt, smtTable)
8584
}
8685
}
8786

8887
/**
8988
* Appends unique constraints for each table to the SMT-LIB.
9089
*
9190
* @param smt The SMT-LIB object to which unique constraints are added.
92-
* @param table The table for which unique constraints are added.
91+
* @param smtTable The table for which unique constraints are added.
9392
*/
94-
private fun appendUniqueConstraints(smt: SMTLib, table: TableDto) {
95-
val smtTableName = convertToAscii(table.id.name).lowercase()
96-
for (column in table.columns) {
93+
private fun appendUniqueConstraints(smt: SMTLib, smtTable: SmtTable) {
94+
for (column in smtTable.dto.columns) {
9795
if (column.unique) {
98-
val nodes = assertForDistinctField(convertToAscii(column.name), smtTableName)
96+
val nodes = assertForDistinctField(smtTable.smtColumnName(column.name), smtTable.smtName)
9997
smt.addNodes(nodes)
10098
}
10199
}
@@ -105,14 +103,14 @@ class SmtLibGenerator(private val schema: DbInfoDto, private val numberOfRows: I
105103
* Appends check constraints for each table to the SMT-LIB.
106104
*
107105
* @param smt The SMT-LIB object to which check constraints are added.
108-
* @param table The table for which check constraints are added.
106+
* @param smtTable The table for which check constraints are added.
109107
*/
110-
private fun appendCheckConstraints(smt: SMTLib, table: TableDto) {
111-
for (check in table.tableCheckExpressions) {
108+
private fun appendCheckConstraints(smt: SMTLib, smtTable: SmtTable) {
109+
for (check in smtTable.dto.tableCheckExpressions) {
112110
try {
113111
val condition: SqlCondition = parser.parse(check.sqlCheckExpression, toDBType(schema.databaseType))
114112
for (i in 1..numberOfRows) {
115-
val constraint: SMTNode = parseCheckExpression(table, condition, i)
113+
val constraint: SMTNode = parseCheckExpression(smtTable, condition, i)
116114
smt.addNode(constraint)
117115
}
118116
} catch (e: SqlConditionParserException) {
@@ -126,13 +124,13 @@ class SmtLibGenerator(private val schema: DbInfoDto, private val numberOfRows: I
126124
/**
127125
* Parses a check expression and returns the corresponding SMT node.
128126
*
129-
* @param table The table containing the check expression.
127+
* @param smtTable The table containing the check expression.
130128
* @param condition The SQL condition to be parsed.
131129
* @param index The index of the row.
132130
* @return The corresponding SMT node.
133131
*/
134-
private fun parseCheckExpression(table: TableDto, condition: SqlCondition, index: Int): SMTNode {
135-
val visitor = SMTConditionVisitor(convertToAscii(table.id.name).lowercase(), emptyMap(), schema.tables, index)
132+
private fun parseCheckExpression(smtTable: SmtTable, condition: SqlCondition, index: Int): SMTNode {
133+
val visitor = SMTConditionVisitor(smtTable.smtName, emptyMap(), schema.tables, index)
136134
return condition.accept(visitor, null) as SMTNode
137135
}
138136

@@ -160,29 +158,28 @@ class SmtLibGenerator(private val schema: DbInfoDto, private val numberOfRows: I
160158
* @param smt The SMT-LIB object to which key constraints are added.
161159
*/
162160
private fun appendKeyConstraints(smt: SMTLib) {
163-
for (table in schema.tables) {
164-
appendPrimaryKeyConstraints(smt, table)
165-
appendForeignKeyConstraints(smt, table)
161+
for (smtTable in smtTables) {
162+
appendPrimaryKeyConstraints(smt, smtTable)
163+
appendForeignKeyConstraints(smt, smtTable)
166164
}
167165
}
168166

169167
private fun appendBooleanConstraints(smt: SMTLib) {
170-
for (table in schema.tables) {
171-
val smtTableName = convertToAscii(table.id.name).lowercase()
172-
for (column in table.columns) {
168+
for (smtTable in smtTables) {
169+
for (column in smtTable.dto.columns) {
173170
if (column.type.equals("BOOLEAN", ignoreCase = true)) {
174-
val columnName = convertToAscii(column.name).uppercase()
171+
val columnName = smtTable.smtColumnName(column.name).uppercase()
175172
for (i in 1..numberOfRows) {
176173
smt.addNode(
177174
AssertSMTNode(
178175
OrAssertion(
179176
listOf(
180-
EqualsAssertion(listOf("($columnName $smtTableName$i)", "\"true\"")),
181-
EqualsAssertion(listOf("($columnName $smtTableName$i)", "\"True\"")),
182-
EqualsAssertion(listOf("($columnName $smtTableName$i)", "\"TRUE\"")),
183-
EqualsAssertion(listOf("($columnName $smtTableName$i)", "\"false\"")),
184-
EqualsAssertion(listOf("($columnName $smtTableName$i)", "\"False\"")),
185-
EqualsAssertion(listOf("($columnName $smtTableName$i)", "\"FALSE\""))
177+
EqualsAssertion(listOf("($columnName ${smtTable.smtName}$i)", "\"true\"")),
178+
EqualsAssertion(listOf("($columnName ${smtTable.smtName}$i)", "\"True\"")),
179+
EqualsAssertion(listOf("($columnName ${smtTable.smtName}$i)", "\"TRUE\"")),
180+
EqualsAssertion(listOf("($columnName ${smtTable.smtName}$i)", "\"false\"")),
181+
EqualsAssertion(listOf("($columnName ${smtTable.smtName}$i)", "\"False\"")),
182+
EqualsAssertion(listOf("($columnName ${smtTable.smtName}$i)", "\"FALSE\""))
186183
)
187184
)
188185
)
@@ -194,27 +191,26 @@ class SmtLibGenerator(private val schema: DbInfoDto, private val numberOfRows: I
194191
}
195192

196193
private fun appendTimestampConstraints(smt: SMTLib) {
197-
for (table in schema.tables) {
198-
val smtTableName = convertToAscii(table.id.name).lowercase()
199-
for (column in table.columns) {
194+
for (smtTable in smtTables) {
195+
for (column in smtTable.dto.columns) {
200196
if (column.type.equals("TIMESTAMP", ignoreCase = true)) {
201-
val columnName = convertToAscii(column.name).uppercase()
197+
val columnName = smtTable.smtColumnName(column.name).uppercase()
202198
val lowerBound = 0 // Example for Unix epoch start
203199
val upperBound = 32503680000 // Example for year 3000 in seconds
204200

205201
for (i in 1..numberOfRows) {
206202
smt.addNode(
207203
AssertSMTNode(
208204
GreaterThanOrEqualsAssertion(
209-
"($columnName $smtTableName$i)",
205+
"($columnName ${smtTable.smtName}$i)",
210206
lowerBound.toString()
211207
)
212208
)
213209
)
214210
smt.addNode(
215211
AssertSMTNode(
216212
LessThanOrEqualsAssertion(
217-
"($columnName $smtTableName$i)",
213+
"($columnName ${smtTable.smtName}$i)",
218214
upperBound.toString()
219215
)
220216
)
@@ -230,14 +226,13 @@ class SmtLibGenerator(private val schema: DbInfoDto, private val numberOfRows: I
230226
* Appends primary key constraints for each table to the SMT-LIB.
231227
*
232228
* @param smt The SMT-LIB object to which primary key constraints are added.
233-
* @param table The table for which primary key constraints are added.
229+
* @param smtTable The table for which primary key constraints are added.
234230
*/
235-
private fun appendPrimaryKeyConstraints(smt: SMTLib, table: TableDto) {
236-
val smtTableName = convertToAscii(table.id.name).lowercase()
237-
val primaryKeys = table.columns.filter { it.primaryKey }
231+
private fun appendPrimaryKeyConstraints(smt: SMTLib, smtTable: SmtTable) {
232+
val primaryKeys = smtTable.dto.columns.filter { it.primaryKey }
238233

239234
for (primaryKey in primaryKeys) {
240-
val nodes = assertForDistinctField(convertToAscii(primaryKey.name), smtTableName)
235+
val nodes = assertForDistinctField(smtTable.smtColumnName(primaryKey.name), smtTable.smtName)
241236
smt.addNodes(nodes)
242237
}
243238
}
@@ -272,20 +267,19 @@ class SmtLibGenerator(private val schema: DbInfoDto, private val numberOfRows: I
272267
* Appends foreign key constraints for each table to the SMT-LIB.
273268
*
274269
* @param smt The SMT-LIB object to which foreign key constraints are added.
275-
* @param table The table for which foreign key constraints are added.
270+
* @param smtTable The table for which foreign key constraints are added.
276271
*/
277-
private fun appendForeignKeyConstraints(smt: SMTLib, table: TableDto) {
278-
val sourceTableName = convertToAscii(table.id.name).lowercase()
279-
280-
for (foreignKey in table.foreignKeys) {
281-
val referencedTable = findReferencedTable(foreignKey)
282-
val referencedTableName = convertToAscii(referencedTable.id.name).lowercase()
283-
val referencedColumnSelector = convertToAscii(findReferencedPKSelector(table, referencedTable, foreignKey))
272+
private fun appendForeignKeyConstraints(smt: SMTLib, smtTable: SmtTable) {
273+
for (foreignKey in smtTable.dto.foreignKeys) {
274+
val referencedSmtTable = findReferencedSmtTable(foreignKey)
275+
val referencedColumnSelector = referencedSmtTable.smtColumnName(
276+
findReferencedPKSelector(smtTable.dto, referencedSmtTable.dto, foreignKey)
277+
)
284278

285279
for (sourceColumn in foreignKey.sourceColumns) {
286280
val nodes = assertForEqualsAny(
287-
convertToAscii(sourceColumn), sourceTableName,
288-
referencedColumnSelector, referencedTableName
281+
smtTable.smtColumnName(sourceColumn), smtTable.smtName,
282+
referencedColumnSelector, referencedSmtTable.smtName
289283
)
290284
smt.addNodes(nodes)
291285
}
@@ -353,13 +347,13 @@ class SmtLibGenerator(private val schema: DbInfoDto, private val numberOfRows: I
353347
}
354348

355349
/**
356-
* Finds the referenced table based on the foreign key.
350+
* Finds the [SmtTable] for the table referenced by the given foreign key.
357351
*
358352
* @param foreignKey The foreign key constraint.
359-
* @return The referenced table.
353+
* @return The referenced [SmtTable].
360354
*/
361-
private fun findReferencedTable(foreignKey: ForeignKeyDto): TableDto {
362-
return schema.tables.firstOrNull { it.id.name.equals(foreignKey.targetTable, ignoreCase = true) }
355+
private fun findReferencedSmtTable(foreignKey: ForeignKeyDto): SmtTable {
356+
return smtTableByOriginalName[foreignKey.targetTable.lowercase()]
363357
?: throw RuntimeException("Referenced table not found: ${foreignKey.targetTable}")
364358
}
365359

@@ -435,7 +429,9 @@ class SmtLibGenerator(private val schema: DbInfoDto, private val numberOfRows: I
435429
* @return The corresponding SMT node.
436430
*/
437431
private fun parseQueryCondition(tableAliases: Map<String, String>, defaultTableName: String, condition: SqlCondition, index: Int): SMTNode {
438-
val visitor = SMTConditionVisitor(convertToAscii(defaultTableName), tableAliases, schema.tables, index)
432+
val smtDefaultTableName = smtTableByOriginalName[defaultTableName.lowercase()]?.smtName
433+
?: convertToAscii(defaultTableName)
434+
val visitor = SMTConditionVisitor(smtDefaultTableName, tableAliases, schema.tables, index)
439435
return condition.accept(visitor, null) as SMTNode
440436
}
441437

@@ -518,12 +514,10 @@ class SmtLibGenerator(private val schema: DbInfoDto, private val numberOfRows: I
518514
}
519515

520516
// Only add GetValueSMTNode for the mentioned tables
521-
for (table in schema.tables) {
522-
val tableNameLower = table.id.name.lowercase()
523-
val smtColumnName = convertToAscii(tableNameLower)
524-
if (tablesMentioned.contains(tableNameLower)) {
517+
for (smtTable in smtTables) {
518+
if (tablesMentioned.contains(smtTable.originalName)) {
525519
for (i in 1..numberOfRows) {
526-
smt.addNode(GetValueSMTNode("$smtColumnName$i"))
520+
smt.addNode(GetValueSMTNode("${smtTable.smtName}$i"))
527521
}
528522
}
529523
}
@@ -532,14 +526,14 @@ class SmtLibGenerator(private val schema: DbInfoDto, private val numberOfRows: I
532526
/**
533527
* Gets the constructors for a table's columns to be used in SMT-LIB.
534528
*
535-
* @param table The table for which constructors are generated.
529+
* @param smtTable The table for which constructors are generated.
536530
* @return A list of SMT nodes for column constructors.
537531
*/
538-
private fun getConstructors(table: TableDto): List<DeclareConstSMTNode> {
539-
return table.columns.map { c ->
532+
private fun getConstructors(smtTable: SmtTable): List<DeclareConstSMTNode> {
533+
return smtTable.dto.columns.map { c ->
540534
val smtType = TYPE_MAP[c.type.uppercase()]
541535
?: throw RuntimeException("Unsupported column type: ${c.type}")
542-
DeclareConstSMTNode(convertToAscii(c.name), smtType)
536+
DeclareConstSMTNode(smtTable.smtColumnName(c.name), smtType)
543537
}
544538
}
545539

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package org.evomaster.core.solver
2+
3+
import org.evomaster.client.java.controller.api.dto.database.schema.TableDto
4+
import org.evomaster.core.utils.StringUtils
5+
import org.evomaster.core.utils.StringUtils.convertToAscii
6+
7+
/**
8+
* A view of a [TableDto] with pre-computed SMT-safe identifiers.
9+
*
10+
* All table and column names are converted to ASCII once at construction time,
11+
* avoiding repeated [convertToAscii] calls throughout [SmtLibGenerator].
12+
*/
13+
class SmtTable(val dto: TableDto) {
14+
15+
/** Original lowercase table name, used to match against SQL query table references. */
16+
val originalName: String = dto.id.name.lowercase()
17+
18+
/** SMT-safe lowercase identifier used in row constant declarations (e.g., "person1", "person2"). */
19+
val smtName: String = convertToAscii(dto.id.name).lowercase()
20+
21+
/** SMT-LIB datatype name for this table's rows (e.g., "PersonRow"). */
22+
val dataTypeName: String = "${StringUtils.capitalization(smtName)}Row"
23+
24+
private val columnSmtNames: Map<String, String> =
25+
dto.columns.associate { col -> col.name to convertToAscii(col.name) }
26+
27+
/** Returns the SMT-safe identifier for the given column name. */
28+
fun smtColumnName(columnName: String): String =
29+
columnSmtNames[columnName] ?: convertToAscii(columnName)
30+
}

0 commit comments

Comments
 (0)