Skip to content

feat: added optional platform selection for driver install - #466

Open
wbeardall wants to merge 1 commit into
columnar-tech:mainfrom
wbeardall:main
Open

feat: added optional platform selection for driver install#466
wbeardall wants to merge 1 commit into
columnar-tech:mainfrom
wbeardall:main

Conversation

@wbeardall

@wbeardall wbeardall commented Aug 22, 2026

Copy link
Copy Markdown

Summary

This is a contribution in response to Issue #50

The idea is to expand platform resolution, enabling users to install drivers which target non-host platforms when using the dbc install command.

Goals and Constraints

  • Do not change default dbc install behaviour; non-host always opt-in
  • WASM lib invocations are unmodified; the WASM / JS interface does not expose platform selection, as this PR is only intended to target
  • Non-host targets are validated ASAP during CLI parse; this does mean that the set of accepted platforms is hard-coded in platform.go, worth considering in case someone ends up adding weird and wonderful supported platforms to the CDN
  • Selected platforms are not validated against the available set of platforms for the specific driver requested (e.g. someone requests windows_arm64 for sqlite. In this case, the standard Error: no package found for platform 'windows_arm64' path occurs.

Potential Concerns and Considerations

The main concern I have currently is that dbc list reads installed manifests without confirming that the driver is actually installed for the host machine. This has the potential for user confusion, as they might have a non-host driver installed, and assume that the entry in dbc list means that they can use that driver with ADBC now!

I deliberately haven't changed the dbc list behaviour, because it has the potential for a bigger direct impact on user experience than the rest of this PR. That said, this is how I'd go about modifying dbc list:

  1. Add a --all-platforms flag to the dbc list command
  2. In absence of this flag, dbc list should filter out any drivers without a host lib explicitly linked in the <driver>.toml, and then print exactly as current
  3. With the --all-platforms flag, the dbc list table would expand with another PLATFORM column, with the host platform being marked explicitly (e.g. linux_amd64 (*))

I've put together a follow-up PR #467 implementing this.

Tests

config/platform_test.go (new file)

TestPlatformUnmarshalText

  • All six valid tuples unmarshal successfully
  • Invalid tuples fail with "unknown platform" and list valid values
    TestPlatformResolve
  • Empty Platform resolves to PlatformTuple() (host)
  • Explicit linux_amd64 resolves to itself

config/config_api_test.go

TestInstallDriver/records_explicit_platform_in_manifest

  • InstallDriver with linux_amd64 keys Driver.shared under that tuple
  • Shared lib file exists on disk
  • Host platform key is empty when it differs from requested platform

cmd/dbc/main_test.go

TestInstallInvalidPlatformRejectedAtParse

  • dbc install --platform noos_noarch mysql fails at argv parse
  • Error contains "unknown platform" and "valid values are:"
    TestInstallHelpMentionsVersionConstraints (extended)
  • Help output includes --platform

cmd/dbc/install_test.go

TestInstallWithPlatform (SubcommandTestSuite)

  • End-to-end CLI install with Platform: linux_amd64
  • Success output, manifest keyed under linux_amd64, file on disk
  • Host platform key absent when it differs from requested platform

client_methods_test.go

TestClientInstall/installs_driver_for_explicit_platform

  • Client.Install(..., config.Platform("linux_amd64")) succeeds
  • Shared path recorded under requested platform key

Updated Tests

TestClientInstall, TestClientUninstall, TestInstallDriver/success,
TestInstallDriver/invalid_tarball — updated for new Install/InstallDriver signatures

Notes

  • Code style: I couldn't see any specific precommit / lint-staged / etc. configurations, so I've not gofmt-ed the PR specifically!
  • Tests all pass on my machine :)

…to host platform, so standard install use-case is unaffected. WASM ops do not expose platform selection.
@zeroshade zeroshade changed the title feat: Added optional platform selection for driver install feat: added optional platform selection for driver install Sep 4, 2026
@zeroshade

Copy link
Copy Markdown
Member

The failing CI for windows is because windows has different behavior on install, using a registry key rather than a toml file on disk, which is handled in config/dirs_windows.go. The CreateManifest function in that file is still hardcoding the host platform tuple instead of using the passed in one. Specifically:

// config/dirs_windows.go:321
setKeyMust(dkey, "driver", driver.Driver.Shared.Get(PlatformTuple()))

Of course, that won't be sufficient to fix the problem. We don't currently persist the platform tuple in the windows registry with the rest of the manifest. It just has a single driver string value in it and assumes that is the default path and will always return the defaultPath for any requested tuple.

So you have to modify the registry usage most likely so that we persist an equivalent to the platformMap into the registry manifest we use.

@zeroshade zeroshade left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall this looks good but there's a structural issue with the behavior.

A cross-platform re-install when a version differs would end up orphaning the original installation. You'll have to re-work how we handle re-installation with cross-platform installs.

Currently, dbc assumes only one platform is ever installed at a time for a given driver. So InstallDriver below always builds a fresh manifest and sets the platform and driverPath, replacing the manifest rather than merging and leveraging the driverMap to hold all the installed platforms. You'll have to change this behavior, and we'll have to decide what we want the behavior to be for the following cases, assuming the user already has Driver D installed, for platform A at version X:

  1. User installs Driver D for platform B at version X
  2. User installs Driver D for platform A at version Y
  3. User installs Driver D for platform B at version Y

Currently, in this PR, all three cases would result in a manifest that only references one platform at one version, potentially leaving behind the extracted tarball from the previous platform without the manifest referencing it for Uninstall to remove.

@amoeba how do you envision each of the scenarios above should result when using the platform option for dbc install?

Conversely, if a user does dbc uninstall should it remove for all platforms? Or do we need to add a --platform option to uninstall as well?

Comment thread cmd/dbc/install.go
Comment on lines 304 to 307
func (m progressiveInstallModel) isAlreadyInstalled() bool {
return m.conflictingInfo.ID != "" && m.conflictingInfo.Version != nil &&
m.conflictingInfo.Version.Equal(m.DriverPackage.Version)
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you need to also update this method to check the platform in addition to the version so that we don't report a driver is already installed if you're installing for a different platform than what is currently installed.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the catch on this!

Comment thread config/platform.go
Comment on lines +29 to +36
var validPlatformTuples = []string{
"linux_amd64",
"linux_arm64",
"macos_amd64",
"macos_arm64",
"windows_amd64",
"windows_arm64",
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At minimum, this should get expanded to cover everything that could be produced by

dbc/config/config.go

Lines 42 to 65 in d85580c

switch os {
case "darwin":
os = "macos"
case "windows", "freebsd", "linux", "openbsd":
default:
os = "unknown"
}
arch := runtime.GOARCH
switch arch {
case "386":
arch = "x86"
case "ppc":
arch = "powerpc"
case "ppc64":
arch = "powerpc64"
case "ppc64le":
arch = "powerpc64le"
case "wasm":
arch = "wasm64"
default:
}
platformTuple = os + "_" + arch

i.e. this is missing freebsd, openbsd, linux_x86, linux_powerpc64le, unknown_wasm64 and others.

If we don't add those platforms here, then a plain dbc install would work on FreeBSD or a x86 host, but dbc install --platform freebsd_amd64 would get rejected. This list shouldn't be kept in line with the registry index (since we already have handling to report when a driver can't be found for a given platform) instead. We should probably just duplicate the platform validation from the stdlib internal package, something like this:

// validPairs maps GOOS to its allowed GOARCH architectures.
// Sourced directly from Go's official internal/platform list.
var validPairs = map[string]map[string]bool{
	"aix":       {"ppc64": true},
	"android":   {"386": true, "amd64": true, "arm": true, "arm64": true},
	"darwin":    {"amd64": true, "arm64": true},
	"dragonfly": {"amd64": true},
	"freebsd":   {"386": true, "amd64": true, "arm": true, "arm64": true, "riscv64": true},
	"illumos":   {"amd64": true},
	"ios":       {"amd64": true, "arm64": true},
	"js":        {"wasm": true},
	"wasip1":    {"wasm": true},
	"linux": {
		"386": true, "amd64": true, "arm": true, "arm64": true,
		"loong64": true, "mips": true, "mips64": true, "mips64le": true,
		"mipsle": true, "ppc64": true, "ppc64le": true, "riscv64": true, "s390x": true,
	},
	"netbsd":  {"386": true, "amd64": true, "arm": true, "arm64": true},
	"openbsd": {"386": true, "amd64": true, "arm": true, "arm64": true},
	"plan9":   {"386": true, "amd64": true, "arm": true},
	"solaris": {"amd64": true},
	"windows": {"386": true, "amd64": true, "arm": true, "arm64": true},
}

// IsValidPlatform verifies if a GOOS/GOARCH combination is compilation-ready.
func IsValidPlatform(platformTuple string) bool {
    goos, goarch, found := strings.Cut(platformTuple, "_")
    if !found {
        return false
    }
	goos = strings.ToLower(strings.TrimSpace(goos))
	goarch = strings.ToLower(strings.TrimSpace(goarch))

	archs, osExists := validPairs[goos]
	if !osExists {
		return false
	}
	return archs[goarch]
}

Obviously doing the swap from darwin -> macos and whatever else we need.

Comment thread cmd/dbc/install.go
driverName := strings.TrimSuffix(
strings.TrimSuffix(filepath.Base(m.Driver), ".tar.gz"), ".tgz")
parts := strings.Split(driverName, "_"+config.PlatformTuple()+"_")
parts := strings.Split(driverName, "_"+m.platform+"_")

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this can cause a problem now.

If you did dbc install ./foo_linux_amd64_1.0.0.tar.gz on macOS, the manifest would record foo_linux_amd64 as the whole driver name and record it as a macOS driver unless you explicitly pass --platform linux_amd64 when doing the local package install. Should we at least attempt to try to derive the platform from the file name first?

Comment thread cmd/dbc/install_test.go
}

func (suite *SubcommandTestSuite) TestInstallWithPlatform() {
const platform = "linux_amd64"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

when this test runs on a linux_amd64 host, it doesn't use the cross-platform branch at all and isn't testing anything useful. Make sure that the tuple we use for this test is guaranteed to differ from PlatformTuple() at runtime

Comment thread client_methods_test.go
})

t.Run("installs driver for explicit platform", func(t *testing.T) {
const platform = "linux_amd64"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same comment as elsewhere, change this so that platform is guaranteed to not match the host PlatformTuple() at runtime regardless of where this is run.

Comment thread client_methods_test.go
assert.NotNil(t, manifest.DriverInfo.Version)
})

t.Run("installs driver for explicit platform", func(t *testing.T) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since the subtests here all share the tmpDir and cfg this test is running against state left by the preceding "installs driver successfully" test. We should probably give this it's own t.TempDir() in this subtest

Comment thread config/platform_test.go
Comment on lines +46 to +48
err := p.UnmarshalText([]byte(tuple))
assert.ErrorContains(t, err, "unknown platform")
assert.ErrorContains(t, err, "valid values are:")

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

include tuple in the message so that if something fails, we'll at least know which input failed.

Also, add assert.Empty(t, p)

Comment thread config/platform.go
Comment on lines +38 to +40
func ValidPlatformTuples() []string {
return slices.Clone(validPlatformTuples)
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nothing is calling this, let's drop this. we don't need it.

Comment thread cmd/dbc/install.go
Comment on lines 315 to 320
payload := jsonschema.InstallStatus{
Status: "already installed",
Driver: m.conflictingInfo.ID,
Version: m.conflictingInfo.Version.String(),
Location: filepath.SplitList(m.cfg.Location)[0],
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should add the platform to this so that the JSON output (using --json) will also get the platform information for what was installed.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants