Skip to content

Can't generate relationship with "RelatedWhere" configuration #553

@absolutejam

Description

@absolutejam

Hey, I've created a minimal reproduction for an issue I was experiencing trying to generate a relationship.

I've created 2 tables and wanted to reference the 'primary version' from the parent. In my example, I have a Locations with many LocationVersions, and one if them is the primary version.

https://github.com/absolutejam/bob-unused-err-repro

CREATE TABLE locations (
    id BIGINT NOT NULL PRIMARY KEY,
    public_id VARCHAR(12) NOT NULL UNIQUE,
    created_at DATETIME NOT NULL,
    updated_at DATETIME,
    deleted_at DATETIME,
    version INTEGER NOT NULL DEFAULT 0
);

CREATE TABLE location_versions (
    id BIGINT NOT NULL PRIMARY KEY,
    public_id VARCHAR(12) NOT NULL UNIQUE,
    is_primary_version BOOLEAN NOT NULL DEFAULT(false),
    location_id BIGINT NOT NULL,
    name TEXT NOT NULL,
    description TEXT,
    picture TEXT,
    created_at DATETIME NOT NULL,
    updated_at DATETIME,
    deleted_at DATETIME,
    version INTEGER NOT NULL DEFAULT 0,

    FOREIGN KEY (location_id) REFERENCES locations (id)
);

Here's the relevant section of my bob config:

aliases:
  location_versions:
    relationships:
      locations_primary_version: "PrimaryVersion"

relationships:
  locations:
    - name: "locations_primary_version"
      never_required: true
      sides:
        - from: "locations"
          to: "location_versions"
          columns:
            - [id, location_id]
          modify: "from"
          to_where:
            - column: "is_primary_version"
              sql_value: "true"
              go_value: "true"

I had to add the alias because otherwise it clashed with the generated FK relationship:

initializing aliases: relationship alias conflict for 'Location' in table 'location_versions': locations_primary_version conflicts with fk_location_versions_0
# location_versions.bob.go
func attachLocationVersionPrimaryVersion0(ctx context.Context, exec bob.Executor, count int, locationVersion0 *LocationVersion) (*LocationVersion, error) {
	setter := &LocationVersionSetter{
		IsPrimaryVersion: &true, # ❌ invalid operation: cannot take address of true (untyped bool constant)
	}

	err := locationVersion0.Update(ctx, exec, setter)
	if err != nil {
		return nil, fmt.Errorf("attachLocationVersionPrimaryVersion0: %w", err)
	}

	return locationVersion0, nil
}

func (locationVersion0 *LocationVersion) InsertPrimaryVersion(ctx context.Context, exec bob.Executor, related *LocationSetter) error {
	locationVersion0, err = attachLocationVersionPrimaryVersion0(ctx, exec, 1, locationVersion0) # ❌ undefined: err
	if err != nil {
		return err
	}

	location1, err := insertLocationVersionPrimaryVersion1(ctx, exec, related, locationVersion0)
	if err != nil {
		return err
	}

	locationVersion0.R.PrimaryVersion = location1

	location1.R.LocationVersion = locationVersion0

	return nil
}
# locations.bob.go
func insertLocationLocationVersion1(ctx context.Context, exec bob.Executor, locationVersion1 *LocationVersionSetter) (*LocationVersion, error) {
	locationVersion1.IsPrimaryVersion = &true # ❌ invalid operation: cannot take address of true (untyped bool constant)

	ret, err := LocationVersions.Insert(locationVersion1).One(ctx, exec)
	if err != nil {
		return ret, fmt.Errorf("insertLocationLocationVersion1: %w", err)
	}

	return ret, nil
}

func attachLocationLocationVersion1(ctx context.Context, exec bob.Executor, count int, locationVersion1 *LocationVersion) (*LocationVersion, error) {
	setter := &LocationVersionSetter{
		IsPrimaryVersion: &true, # ❌ invalid operation: cannot take address of true (untyped bool constant)
	}

	err := locationVersion1.Update(ctx, exec, setter)
	if err != nil {
		return nil, fmt.Errorf("attachLocationLocationVersion1: %w", err)
	}

	return locationVersion1, nil
}

func buildLocationVersionPreloader() locationVersionPreloader {
	return locationVersionPreloader{
		Location: func(opts ...sqlite.PreloadOption) sqlite.Preloader {
			return sqlite.Preload[*Location, LocationSlice](sqlite.PreloadRel{
				Name: "Location",
				Sides: []sqlite.PreloadSide{
					{
						From:        LocationVersions,
						To:          Locations,
						FromColumns: []string{"location_id"},
						ToColumns:   []string{"id"},
					},
				},
			}, Locations.Columns.Names(), opts...)
		},
		PrimaryVersion: func(opts ...sqlite.PreloadOption) sqlite.Preloader {
			return sqlite.Preload[*Location, LocationSlice](sqlite.PreloadRel{
				Name: "PrimaryVersion",
				Sides: []sqlite.PreloadSide{
					{
						From:        LocationVersions,
						To:          Locations,
						FromColumns: []string{"location_id"},
						ToColumns:   []string{"id"},
						FromWhere: []orm.RelWhere{
							{
								Column:   ColumnNames.LocationVersions.IsPrimaryVersion, # ❌ undefined: ColumnNames
								SQLValue: "`true`",
								GoValue:  "true",
							},
						},
					},
				},
			}, Locations.Columns.Names(), opts...)
		},
	}
}

I also unquoted "true" but that leads to a 1 in SQLite, and that just ends up with the same issue of trying to take the pointer to a literal (&1).

I'll see if I can fork and resolve this, but thought I'd share it here for visibility too.

For these kind of things, I assume the generator would want to have a static value, eg. var locationsPrimaryVersionToWhereValue = true and use that instead of the literal?

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions