Skip to content

Parameter object in Main.kt; no nullable defaults in JdbcAnalyzer.buildParameters #283

Description

Why

Two small boundary smells in the generator's orchestration layer.

Main.kt. generateQueryImplementation (Main.kt:108-200) takes nine positional parameters, six
of which exist only to compute the PostgresQueries constructor's adapter parameters inside it.
generateCode (Main.kt:46-106) holds a TypeRepository in a variable named generator, and depends
on an ordering side effect it has to explain in a comment (Main.kt:55-56: "discoveredEnums is
populated as a side effect of resolving column types above"). SqlStatement also names its
TypeRepository field generator (SqlStatement.kt:18).

JdbcAnalyzer.buildParameters (JdbcAnalyzer.kt:267-273) declares inferredNames,
notNullByParameter, inferredParameters with emptyMap() defaults and catalog: Catalog? = null,
purely so the CALL fallback path (analyzeCallParameters, :336-338) can call it with one argument.
The catalog != null checks at :121 and :314 exist only to serve that one caller.

Target state

Main.kt

/** One `PostgresQueries` constructor parameter carrying a ColumnAdapter. */
private data class AdapterParameter(val propertyName: String, val adapterType: TypeName, val defaultClass: ClassName?)

/**
 * The adapter parameters `PostgresQueries` needs: every user-configured mapping (no default, first),
 * then every enum and domain [typeRepository] saw while resolving the queries (with a default,
 * sorted). Must be called after every query has been resolved, or later discoveries are missed.
 */
private fun adapterParameters(
  typeRepository: TypeRepository,
  typeMappings: List<TypeMapping>,
  catalog: Catalog,
  packageName: String,
): List<AdapterParameter>

private fun generateQueryImplementation(
  queries: List<SqlStatement>,
  interfaceType: ClassName,
  frameworks: Set<Framework>,
  adapterParameters: List<AdapterParameter>,
): TypeSpec

generateCode becomes: build typeRepository; resolve queries into SqlStatements; compute
typeOverridePostgresTypes; compute adapterParameters(...); build interface, enums, domains,
implementation, connection providers. The ordering requirement is stated once on
adapterParameters' KDoc and nowhere else. Rename generatortypeRepository in Main.kt and
SqlStatement.kt.

Move typeOverridePostgresTypes filtering into adapterParameters (it is the only remaining
consumer besides the two filter { it.name !in ... } calls, which can take the same set from a
single local).

JdbcAnalyzer

private fun buildParameters(
  pmd: java.sql.ParameterMetaData,
  inferredNames: Map<Int, String>,
  notNullByParameter: Map<Int, Boolean>,
  inferredParameters: Map<Int, InferredParameter>,
  catalog: Catalog,
): List<Parameter>

No defaults, catalog non-null. analyzeCallParameters(originalSql, jdbcSql, catalog) passes
emptyMap(), emptyMap(), emptyMap(), catalog. The two catalog != null && guards disappear; the
tableName != null && columnName != null guards stay.

Test design

  • GenerateCodeTest.kt and FrameworkAnnotationTest.kt assert on the generated PostgresQueries
    constructor (adapter parameter order, defaults, DI annotations); they pin Main.kt. Add one test in
    GenerateCodeTest that a schema with an enum referenced only by the last query still produces the
    enum adapter parameter (pins the "after every query is resolved" contract explicitly rather than by
    accident of scenario ordering).
  • JdbcAnalyzerTest.kt has CALL statement tests (grep CALL ); they pin analyzeCallParameters
    including the fallback branch. Confirm one covers a procedure not found in pg_proc so the
    prepareStatement fallback runs; add if missing.
  • Golden scenarios type_mappings, domains, all_types pin constructor output byte-for-byte.

Acceptance criteria

  1. No function in Main.kt has more than five parameters.
  2. git grep -n "generator\b" generator/src/main/kotlin/norm/generator/Main.kt generator/src/main/kotlin/norm/generator/SqlStatement.kt matches only the package name.
  3. buildParameters has no default arguments and no nullable parameter.
  4. ./gradlew :generator:check :gradle-plugin:test passes; golden regeneration leaves
    test-scenarios* byte-identical.

Files

  • generator/src/main/kotlin/norm/generator/Main.kt
  • generator/src/main/kotlin/norm/generator/SqlStatement.kt
  • generator/src/main/kotlin/norm/generator/JdbcAnalyzer.kt
  • generator/src/test/kotlin/norm/generator/GenerateCodeTest.kt, JdbcAnalyzerTest.kt

Conventions every issue inherits

  • Repo root: /Volumes/Code/3rd-party/norm. Module under change is almost always generator/.
  • Style: 2-space indent, 120 columns, full words in identifiers (parameter, not param), no
    section-separator comments, @Nested classes group tests. Format with ./gradlew spotlessApply.
  • TDD: tests are written first or alongside. For a behavior-preserving refactor the existing suite is
    the pin; each issue says which tests must also be added.
  • Golden files under test-scenarios*/ are never hand-edited. A refactor is behavior-preserving only
    if ./gradlew :gradle-plugin:generateGoldenFiles leaves git status --porcelain test-scenarios test-scenarios-frameworks empty.
  • Verification for a generator/ change: ./gradlew :generator:check :gradle-plugin:test (Docker
    required). Then the golden regeneration check above.
  • Commit message explains the design decision, not the diff.
  • Never run ./gradlew clean or disable the configuration/build cache to "fix" a build problem.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions