@@ -15,11 +15,11 @@ import org.evomaster.core.utils.StringUtils
1515import org.evomaster.dbconstraint.ConstraintDatabaseType
1616import org.evomaster.dbconstraint.ast.SqlCondition
1717import net.sf.jsqlparser.JSQLParserException
18+ import org.evomaster.core.utils.StringUtils.convertToAscii
1819import org.evomaster.dbconstraint.parser.SqlConditionParserException
1920import org.evomaster.dbconstraint.parser.jsql.JSqlConditionParser
2021import org.evomaster.solver.smtlib.*
2122import org.evomaster.solver.smtlib.assertion.*
22- import java.util.*
2323
2424/* *
2525 * Generates SMT-LIB constraints from SQL queries and schema definitions.
@@ -58,7 +58,7 @@ class SmtLibGenerator(private val schema: DbInfoDto, private val numberOfRows: I
5858 */
5959 private fun appendTableDefinitions (smt : SMTLib ) {
6060 for (table in schema.tables) {
61- val sanitizedTableName = sanitizeSmtIdentifier (table.id.name)
61+ val sanitizedTableName = convertToAscii (table.id.name)
6262 val dataTypeName = " ${StringUtils .capitalization(sanitizedTableName)} Row"
6363
6464 // Declare datatype for the table
@@ -92,10 +92,10 @@ class SmtLibGenerator(private val schema: DbInfoDto, private val numberOfRows: I
9292 * @param table The table for which unique constraints are added.
9393 */
9494 private fun appendUniqueConstraints (smt : SMTLib , table : TableDto ) {
95- val tableName = sanitizeSmtIdentifier (table.id.name).lowercase()
95+ val tableName = convertToAscii (table.id.name).lowercase()
9696 for (column in table.columns) {
9797 if (column.unique) {
98- val nodes = assertForDistinctField(sanitizeSmtIdentifier (column.name), tableName)
98+ val nodes = assertForDistinctField(convertToAscii (column.name), tableName)
9999 smt.addNodes(nodes)
100100 }
101101 }
@@ -132,7 +132,7 @@ class SmtLibGenerator(private val schema: DbInfoDto, private val numberOfRows: I
132132 * @return The corresponding SMT node.
133133 */
134134 private fun parseCheckExpression (table : TableDto , condition : SqlCondition , index : Int ): SMTNode {
135- val visitor = SMTConditionVisitor (sanitizeSmtIdentifier (table.id.name).lowercase(), emptyMap(), schema.tables, index)
135+ val visitor = SMTConditionVisitor (convertToAscii (table.id.name).lowercase(), emptyMap(), schema.tables, index)
136136 return condition.accept(visitor, null ) as SMTNode
137137 }
138138
@@ -168,10 +168,10 @@ class SmtLibGenerator(private val schema: DbInfoDto, private val numberOfRows: I
168168
169169 private fun appendBooleanConstraints (smt : SMTLib ) {
170170 for (table in schema.tables) {
171- val tableName = sanitizeSmtIdentifier (table.id.name).lowercase()
171+ val tableName = convertToAscii (table.id.name).lowercase()
172172 for (column in table.columns) {
173173 if (column.type.equals(" BOOLEAN" , ignoreCase = true )) {
174- val columnName = sanitizeSmtIdentifier (column.name).uppercase()
174+ val columnName = convertToAscii (column.name).uppercase()
175175 for (i in 1 .. numberOfRows) {
176176 smt.addNode(
177177 AssertSMTNode (
@@ -195,10 +195,10 @@ class SmtLibGenerator(private val schema: DbInfoDto, private val numberOfRows: I
195195
196196 private fun appendTimestampConstraints (smt : SMTLib ) {
197197 for (table in schema.tables) {
198- val tableName = sanitizeSmtIdentifier (table.id.name).lowercase()
198+ val tableName = convertToAscii (table.id.name).lowercase()
199199 for (column in table.columns) {
200200 if (column.type.equals(" TIMESTAMP" , ignoreCase = true )) {
201- val columnName = sanitizeSmtIdentifier (column.name).uppercase()
201+ val columnName = convertToAscii (column.name).uppercase()
202202 val lowerBound = 0 // Example for Unix epoch start
203203 val upperBound = 32503680000 // Example for year 3000 in seconds
204204
@@ -233,11 +233,11 @@ class SmtLibGenerator(private val schema: DbInfoDto, private val numberOfRows: I
233233 * @param table The table for which primary key constraints are added.
234234 */
235235 private fun appendPrimaryKeyConstraints (smt : SMTLib , table : TableDto ) {
236- val tableName = sanitizeSmtIdentifier (table.id.name).lowercase()
236+ val tableName = convertToAscii (table.id.name).lowercase()
237237 val primaryKeys = table.columns.filter { it.primaryKey }
238238
239239 for (primaryKey in primaryKeys) {
240- val nodes = assertForDistinctField(sanitizeSmtIdentifier (primaryKey.name), tableName)
240+ val nodes = assertForDistinctField(convertToAscii (primaryKey.name), tableName)
241241 smt.addNodes(nodes)
242242 }
243243 }
@@ -275,16 +275,16 @@ class SmtLibGenerator(private val schema: DbInfoDto, private val numberOfRows: I
275275 * @param table The table for which foreign key constraints are added.
276276 */
277277 private fun appendForeignKeyConstraints (smt : SMTLib , table : TableDto ) {
278- val sourceTableName = sanitizeSmtIdentifier (table.id.name).lowercase()
278+ val sourceTableName = convertToAscii (table.id.name).lowercase()
279279
280280 for (foreignKey in table.foreignKeys) {
281281 val referencedTable = findReferencedTable(foreignKey)
282- val referencedTableName = sanitizeSmtIdentifier (referencedTable.id.name).lowercase()
283- val referencedColumnSelector = sanitizeSmtIdentifier (findReferencedPKSelector(table, referencedTable, foreignKey))
282+ val referencedTableName = convertToAscii (referencedTable.id.name).lowercase()
283+ val referencedColumnSelector = convertToAscii (findReferencedPKSelector(table, referencedTable, foreignKey))
284284
285285 for (sourceColumn in foreignKey.sourceColumns) {
286286 val nodes = assertForEqualsAny(
287- sanitizeSmtIdentifier (sourceColumn), sourceTableName,
287+ convertToAscii (sourceColumn), sourceTableName,
288288 referencedColumnSelector, referencedTableName
289289 )
290290 smt.addNodes(nodes)
@@ -435,7 +435,7 @@ class SmtLibGenerator(private val schema: DbInfoDto, private val numberOfRows: I
435435 * @return The corresponding SMT node.
436436 */
437437 private fun parseQueryCondition (tableAliases : Map <String , String >, defaultTableName : String , condition : SqlCondition , index : Int ): SMTNode {
438- val visitor = SMTConditionVisitor (sanitizeSmtIdentifier (defaultTableName), tableAliases, schema.tables, index)
438+ val visitor = SMTConditionVisitor (convertToAscii (defaultTableName), tableAliases, schema.tables, index)
439439 return condition.accept(visitor, null ) as SMTNode
440440 }
441441
@@ -520,10 +520,10 @@ class SmtLibGenerator(private val schema: DbInfoDto, private val numberOfRows: I
520520 // Only add GetValueSMTNode for the mentioned tables
521521 for (table in schema.tables) {
522522 val tableNameLower = table.id.name.lowercase()
523- val sanitizedTableNameLower = sanitizeSmtIdentifier (tableNameLower)
523+ val smtColumnName = convertToAscii (tableNameLower)
524524 if (tablesMentioned.contains(tableNameLower)) {
525525 for (i in 1 .. numberOfRows) {
526- smt.addNode(GetValueSMTNode (" $sanitizedTableNameLower $i " ))
526+ smt.addNode(GetValueSMTNode (" $smtColumnName $i " ))
527527 }
528528 }
529529 }
@@ -539,29 +539,11 @@ class SmtLibGenerator(private val schema: DbInfoDto, private val numberOfRows: I
539539 return table.columns.map { c ->
540540 val smtType = TYPE_MAP [c.type.uppercase()]
541541 ? : throw RuntimeException (" Unsupported column type: ${c.type} " )
542- DeclareConstSMTNode (sanitizeSmtIdentifier (c.name), smtType)
542+ DeclareConstSMTNode (convertToAscii (c.name), smtType)
543543 }
544544 }
545545
546546 companion object {
547- /* *
548- * Replaces non-ASCII characters in a name to make it a valid SMT-LIB identifier.
549- * SMT-LIB unquoted symbols are restricted to ASCII, so characters like Æ, Ø, Å must be transliterated.
550- *
551- * This is needed because our test suite includes Norwegian APIs whose database schemas
552- * contain column and table names with Norwegian characters (Æ, Ø, Å).
553- *
554- * Characters that do not decompose under NFD (Ø, Æ) are replaced explicitly.
555- * Characters that decompose under NFD (Å→A, and other accented letters like é, ü, ñ)
556- * are handled by normalizing to NFD form and stripping the remaining non-ASCII combining marks.
557- */
558- fun sanitizeSmtIdentifier (name : String ): String {
559- val replaced = name
560- .replace(' Ø' , ' O' ).replace(' ø' , ' o' )
561- .replace(" Æ" , " AE" ).replace(" æ" , " ae" )
562- return java.text.Normalizer .normalize(replaced, java.text.Normalizer .Form .NFD )
563- .replace(Regex (" [^\\ x00-\\ x7F]" ), " " )
564- }
565547
566548 // Maps database column types to SMT-LIB types
567549 private val TYPE_MAP = mapOf (
0 commit comments