@@ -169,20 +169,15 @@ class SmtLibGenerator(private val schema: DbInfoDto, private val numberOfRows: I
169169 private fun appendBooleanConstraints (smt : SMTLib ) {
170170 for (smtTable in smtTables) {
171171 for (column in smtTable.dto.columns) {
172- if (column.type.equals(" BOOLEAN " , ignoreCase = true )) {
172+ if (column.type.equals(BOOLEAN_TYPE , ignoreCase = true )) {
173173 val columnName = smtTable.smtColumnName(column.name).uppercase()
174174 for (i in 1 .. numberOfRows) {
175175 smt.addNode(
176176 AssertSMTNode (
177177 OrAssertion (
178- listOf (
179- EqualsAssertion (listOf (" ($columnName ${smtTable.smtName}$i )" , " \" true\" " )),
180- EqualsAssertion (listOf (" ($columnName ${smtTable.smtName}$i )" , " \" True\" " )),
181- EqualsAssertion (listOf (" ($columnName ${smtTable.smtName}$i )" , " \" TRUE\" " )),
182- EqualsAssertion (listOf (" ($columnName ${smtTable.smtName}$i )" , " \" false\" " )),
183- EqualsAssertion (listOf (" ($columnName ${smtTable.smtName}$i )" , " \" False\" " )),
184- EqualsAssertion (listOf (" ($columnName ${smtTable.smtName}$i )" , " \" FALSE\" " ))
185- )
178+ BOOLEAN_LITERALS .map { literal ->
179+ EqualsAssertion (listOf (" ($columnName ${smtTable.smtName}$i )" , " \" $literal \" " ))
180+ }
186181 )
187182 )
188183 )
@@ -195,25 +190,23 @@ class SmtLibGenerator(private val schema: DbInfoDto, private val numberOfRows: I
195190 private fun appendTimestampConstraints (smt : SMTLib ) {
196191 for (smtTable in smtTables) {
197192 for (column in smtTable.dto.columns) {
198- if (column.type.equals(" TIMESTAMP " , ignoreCase = true )) {
193+ if (column.type.equals(TIMESTAMP_TYPE , ignoreCase = true )) {
199194 val columnName = smtTable.smtColumnName(column.name).uppercase()
200- val lowerBound = 0 // Example for Unix epoch start
201- val upperBound = 32503680000 // Example for year 3000 in seconds
202195
203196 for (i in 1 .. numberOfRows) {
204197 smt.addNode(
205198 AssertSMTNode (
206199 GreaterThanOrEqualsAssertion (
207200 " ($columnName ${smtTable.smtName}$i )" ,
208- lowerBound .toString()
201+ TIMESTAMP_EPOCH_LOWER_BOUND .toString()
209202 )
210203 )
211204 )
212205 smt.addNode(
213206 AssertSMTNode (
214207 LessThanOrEqualsAssertion (
215208 " ($columnName ${smtTable.smtName}$i )" ,
216- upperBound .toString()
209+ TIMESTAMP_EPOCH_UPPER_BOUND .toString()
217210 )
218211 )
219212 )
@@ -604,44 +597,62 @@ class SmtLibGenerator(private val schema: DbInfoDto, private val numberOfRows: I
604597
605598 companion object {
606599
600+ // Bounds for TIMESTAMP columns, encoded as epoch seconds (SMT Int).
601+ private const val TIMESTAMP_EPOCH_LOWER_BOUND = 0L // Unix epoch start
602+ private const val TIMESTAMP_EPOCH_UPPER_BOUND = 32503680000L // ~year 3000, in seconds
603+
604+ // SMT-LIB sorts used as TYPE_MAP targets.
605+ private const val SMT_INT = " Int"
606+ private const val SMT_REAL = " Real"
607+ private const val SMT_STRING = " String"
608+
609+ // SQL type names that need special interpretation beyond their SMT sort: BOOLEAN is encoded as an
610+ // SMT String and TIMESTAMP as an SMT Int, so gene reconstruction must consult the original type.
611+ // Shared with the comparison sites in this class and referenced by SMTLibZ3DbConstraintSolver.
612+ const val BOOLEAN_TYPE = " BOOLEAN"
613+ const val TIMESTAMP_TYPE = " TIMESTAMP"
614+
615+ // The string values a BOOLEAN column may take (BOOLEAN is encoded as an SMT String).
616+ private val BOOLEAN_LITERALS = listOf (" true" , " True" , " TRUE" , " false" , " False" , " FALSE" )
617+
607618 // Maps database column types to SMT-LIB types.
608619 // FUTURE WORK: this is one of three independent type vocabularies interpreting ColumnDto.type
609620 // (the others are SMTLibZ3DbConstraintSolver.getColumnDataType and .hasColumnType). They can
610621 // silently disagree when a backend reports a variant spelling; consolidating them into a single
611622 // source of truth is future work (see the note on SMTLibZ3DbConstraintSolver.hasColumnType).
612623 private val TYPE_MAP = mapOf (
613- " BIGINT" to " Int " ,
614- " BIT" to " Int " ,
615- " INTEGER" to " Int " ,
616- " INT" to " Int " ,
617- " INT2" to " Int " ,
618- " INT4" to " Int " ,
619- " INT8" to " Int " ,
620- " TINYINT" to " Int " ,
621- " SMALLINT" to " Int " ,
624+ " BIGINT" to SMT_INT ,
625+ " BIT" to SMT_INT ,
626+ " INTEGER" to SMT_INT ,
627+ " INT" to SMT_INT ,
628+ " INT2" to SMT_INT ,
629+ " INT4" to SMT_INT ,
630+ " INT8" to SMT_INT ,
631+ " TINYINT" to SMT_INT ,
632+ " SMALLINT" to SMT_INT ,
622633 // KNOWN LIMITATION: NUMERIC is mapped to Int, so any fractional part is truncated. This is
623634 // inconsistent with DECIMAL (mapped to Real). Mapping NUMERIC to Real (to preserve decimals)
624635 // is future work.
625- " NUMERIC" to " Int " ,
626- " SERIAL" to " Int " ,
627- " SMALLSERIAL" to " Int " ,
628- " BIGSERIAL" to " Int " ,
629- " TIMESTAMP " to " Int " ,
630- " DATE" to " Int " ,
631- " FLOAT" to " Real " ,
632- " DOUBLE" to " Real " ,
633- " DECIMAL" to " Real " ,
634- " REAL" to " Real " ,
635- " CHARACTER VARYING" to " String " ,
636- " CHAR" to " String " ,
637- " VARCHAR" to " String " ,
638- " TEXT" to " String " ,
639- " CHARACTER LARGE OBJECT" to " String " ,
640- " BOOLEAN " to " String " ,
641- " BOOL" to " String " ,
642- " UUID" to " String " ,
643- " JSONB" to " String " ,
644- " BYTEA" to " String " ,
636+ " NUMERIC" to SMT_INT ,
637+ " SERIAL" to SMT_INT ,
638+ " SMALLSERIAL" to SMT_INT ,
639+ " BIGSERIAL" to SMT_INT ,
640+ TIMESTAMP_TYPE to SMT_INT ,
641+ " DATE" to SMT_INT ,
642+ " FLOAT" to SMT_REAL ,
643+ " DOUBLE" to SMT_REAL ,
644+ " DECIMAL" to SMT_REAL ,
645+ " REAL" to SMT_REAL ,
646+ " CHARACTER VARYING" to SMT_STRING ,
647+ " CHAR" to SMT_STRING ,
648+ " VARCHAR" to SMT_STRING ,
649+ " TEXT" to SMT_STRING ,
650+ " CHARACTER LARGE OBJECT" to SMT_STRING ,
651+ BOOLEAN_TYPE to SMT_STRING ,
652+ " BOOL" to SMT_STRING ,
653+ " UUID" to SMT_STRING ,
654+ " JSONB" to SMT_STRING ,
655+ " BYTEA" to SMT_STRING ,
645656 )
646657 }
647658}
0 commit comments