This is an automated email from the ASF dual-hosted git repository.

lidavidm pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/arrow-adbc.git


The following commit(s) were added to refs/heads/main by this push:
     new 254ea1cfe feat(go/adbc/driver/flightsql): add support for flightsql:// 
uri format (#4488)
254ea1cfe is described below

commit 254ea1cfe7c621f76c7ec33b59d0bb73f021e8e6
Author: Mandukhai Alimaa <[email protected]>
AuthorDate: Tue Jul 14 16:46:58 2026 -0700

    feat(go/adbc/driver/flightsql): add support for flightsql:// uri format 
(#4488)
    
    Closes #4460.
---
 docs/source/driver/flight_sql.rst               | 43 ++++++++++++-
 go/adbc/driver/flightsql/flightsql_adbc_test.go | 80 +++++++++++++++++++++++++
 go/adbc/driver/flightsql/flightsql_database.go  | 24 ++++++++
 go/adbc/driver/flightsql/flightsql_driver.go    |  9 ++-
 go/adbc/sqldriver/flightsql/README.md           |  2 +-
 r/adbcflightsql/README.Rmd                      |  3 +-
 r/adbcflightsql/README.md                       |  3 +-
 7 files changed, 158 insertions(+), 6 deletions(-)

diff --git a/docs/source/driver/flight_sql.rst 
b/docs/source/driver/flight_sql.rst
index 75513a6da..29f871ec7 100644
--- a/docs/source/driver/flight_sql.rst
+++ b/docs/source/driver/flight_sql.rst
@@ -48,7 +48,7 @@ the :c:struct:`AdbcDatabase`.
          struct AdbcDatabase database;
          AdbcDatabaseNew(&database, nullptr);
          AdbcDatabaseSetOption(&database, "driver", "adbc_driver_flightsql", 
nullptr);
-         AdbcDatabaseSetOption(&database, "uri", "grpc://localhost:8080", 
nullptr);
+         AdbcDatabaseSetOption(&database, "uri", 
"flightsql://localhost:8080?transport=tcp", nullptr);
          AdbcDatabaseInit(&database, nullptr);
 
    .. tab-item:: Python
@@ -64,7 +64,7 @@ the :c:struct:`AdbcDatabase`.
          headers = {"foo": "bar"}
 
          with connect(
-             "grpc+tls://localhost:8080",
+             "flightsql://localhost:8080",
              db_kwargs={
                  DatabaseOptions.AUTHORIZATION_HEADER.value: "Bearer <token>",
                  DatabaseOptions.TLS_SKIP_VERIFY.value: "true",
@@ -81,6 +81,45 @@ the :c:struct:`AdbcDatabase`.
 
       .. recipe:: ../../../go/adbc/driver/flightsql/example_usage_test.go
 
+URI Format
+----------
+
+The "uri" option accepts URIs using the ``flightsql://`` scheme.  The
+transport is selected with the ``transport`` query parameter, which is
+matched case-insensitively:
+
+``flightsql://<host>:<port>``
+    Connect over gRPC with TLS (secure).  This is the default when no
+    ``transport`` query parameter is given.
+
+``flightsql://<host>:<port>?transport=tls``
+    Same as above; an explicit way to request TLS (secure).
+
+``flightsql://<host>:<port>?transport=tcp``
+    Connect over gRPC without TLS (plaintext).
+
+``flightsql:///<path/to/socket>?transport=unix``
+    Connect over gRPC via a Unix domain socket at the given path.
+
+An unrecognized ``transport`` value (anything other than ``tls``,
+``tcp``, or ``unix``) is rejected with an error rather than silently
+falling back to a default.  Similarly, supplying a host with
+``transport=unix``, or a socket path with ``transport=tcp`` or the
+default TLS transport, is rejected as an invalid combination.
+
+.. note:: For backwards compatibility, the driver also still accepts
+          the following legacy schemes, each equivalent to a
+          ``flightsql://`` URI with the given ``transport``:
+
+          =====================  =========================
+          Legacy scheme           Equivalent ``transport``
+          =====================  =========================
+          ``grpc://``              ``tcp``
+          ``grpc+tcp://``          ``tcp``
+          ``grpc+tls://``          ``tls`` (default)
+          ``grpc+unix://``         ``unix``
+          =====================  =========================
+
 Supported Features
 ==================
 
diff --git a/go/adbc/driver/flightsql/flightsql_adbc_test.go 
b/go/adbc/driver/flightsql/flightsql_adbc_test.go
index 92c00e5b4..2be9496e9 100644
--- a/go/adbc/driver/flightsql/flightsql_adbc_test.go
+++ b/go/adbc/driver/flightsql/flightsql_adbc_test.go
@@ -1001,6 +1001,32 @@ func (suite *TLSTests) TestSimpleQuery() {
        suite.NoError(reader.Err())
 }
 
+func (suite *TLSTests) TestFlightSQLSchemeDefaultTLS() {
+       db, err := suite.Driver.NewDatabase(map[string]string{
+               adbc.OptionKeyURI: "flightsql://" + 
suite.Quirks.s.Addr().String(),
+               "adbc.flight.sql.client_option.tls_skip_verify": "true",
+       })
+       suite.Require().NoError(err)
+       defer validation.CheckedClose(suite.T(), db)
+
+       cnxn, err := db.Open(suite.ctx)
+       suite.Require().NoError(err)
+       defer validation.CheckedClose(suite.T(), cnxn)
+}
+
+func (suite *TLSTests) TestFlightSQLSchemeExplicitTLS() {
+       db, err := suite.Driver.NewDatabase(map[string]string{
+               adbc.OptionKeyURI: "flightsql://" + 
suite.Quirks.s.Addr().String() + "?transport=tls",
+               "adbc.flight.sql.client_option.tls_skip_verify": "true",
+       })
+       suite.Require().NoError(err)
+       defer validation.CheckedClose(suite.T(), db)
+
+       cnxn, err := db.Open(suite.ctx)
+       suite.Require().NoError(err)
+       defer validation.CheckedClose(suite.T(), cnxn)
+}
+
 func (suite *TLSTests) TestInvalidOptions() {
        db, err := suite.Driver.NewDatabase(map[string]string{
                adbc.OptionKeyURI: "grpc+tls://" + 
suite.Quirks.s.Addr().String(),
@@ -1102,6 +1128,18 @@ func (suite *ConnectionTests) TestGetInfo() {
        suite.Require().True(driverArrowVersion)
 }
 
+func (suite *ConnectionTests) TestFlightSQLSchemeTCP() {
+       db, err := suite.Driver.NewDatabase(map[string]string{
+               adbc.OptionKeyURI: "flightsql://" + 
suite.server.Addr().String() + "?transport=tcp",
+       })
+       suite.Require().NoError(err)
+       defer validation.CheckedClose(suite.T(), db)
+
+       cnxn, err := db.Open(suite.ctx)
+       suite.Require().NoError(err)
+       defer validation.CheckedClose(suite.T(), cnxn)
+}
+
 type DomainSocketTests struct {
        suite.Suite
 
@@ -1189,3 +1227,45 @@ func (suite *DomainSocketTests) 
TestSimpleQueryDomainSocket() {
        }
        suite.NoError(reader.Err())
 }
+
+func (suite *DomainSocketTests) TestFlightSQLSchemeUnixSocket() {
+       db, err := suite.Driver.NewDatabase(map[string]string{
+               adbc.OptionKeyURI: "flightsql://" + 
suite.server.Addr().String() + "?transport=unix",
+       })
+       suite.Require().NoError(err)
+       defer validation.CheckedClose(suite.T(), db)
+
+       cnxn, err := db.Open(suite.ctx)
+       suite.Require().NoError(err)
+       defer validation.CheckedClose(suite.T(), cnxn)
+}
+
+func TestFlightSQLSchemeInvalidURIs(t *testing.T) {
+       alloc := memory.NewCheckedAllocator(memory.DefaultAllocator)
+       defer alloc.AssertSize(t, 0)
+       drv := driver.NewDriver(alloc)
+
+       tests := []struct {
+               name string
+               uri  string
+       }{
+               {"unrecognized transport", 
"flightsql://localhost:1234?transport=bogus"},
+               {"host with unix transport", 
"flightsql://localhost:1234/socket?transport=unix"},
+               {"path with tcp transport", 
"flightsql://localhost:1234/socket?transport=tcp"},
+               {"path with default (tls) transport", 
"flightsql://localhost:1234/socket"},
+               {"unix transport without a path", 
"flightsql://?transport=unix"},
+       }
+
+       for _, tt := range tests {
+               t.Run(tt.name, func(t *testing.T) {
+                       db, err := drv.NewDatabase(map[string]string{
+                               adbc.OptionKeyURI: tt.uri,
+                       })
+                       require.NoError(t, err)
+                       defer validation.CheckedClose(t, db)
+
+                       _, err = db.Open(context.Background())
+                       require.Error(t, err)
+               })
+       }
+}
diff --git a/go/adbc/driver/flightsql/flightsql_database.go 
b/go/adbc/driver/flightsql/flightsql_database.go
index 15b2f90c2..185e2a22d 100644
--- a/go/adbc/driver/flightsql/flightsql_database.go
+++ b/go/adbc/driver/flightsql/flightsql_database.go
@@ -407,6 +407,30 @@ func getFlightClient(ctx context.Context, loc string, d 
*databaseImpl, authMiddl
        case "grpc+unix":
                creds = insecure.NewCredentials()
                target = "unix:" + uri.Path
+       case "flightsql":
+               transport := strings.ToLower(uri.Query().Get("transport"))
+               switch transport {
+               case "", "tls":
+                       if uri.Path != "" {
+                               return nil, adbc.Error{Msg: 
fmt.Sprintf("Invalid URI '%s': a socket path is only valid with 
transport=unix", loc), Code: adbc.StatusInvalidArgument}
+                       }
+               case "tcp":
+                       if uri.Path != "" {
+                               return nil, adbc.Error{Msg: 
fmt.Sprintf("Invalid URI '%s': a socket path is only valid with 
transport=unix", loc), Code: adbc.StatusInvalidArgument}
+                       }
+                       creds = insecure.NewCredentials()
+               case "unix":
+                       if uri.Host != "" {
+                               return nil, adbc.Error{Msg: 
fmt.Sprintf("Invalid URI '%s': a host is not valid with transport=unix", loc), 
Code: adbc.StatusInvalidArgument}
+                       }
+                       if uri.Path == "" {
+                               return nil, adbc.Error{Msg: 
fmt.Sprintf("Invalid URI '%s': transport=unix requires a socket path", loc), 
Code: adbc.StatusInvalidArgument}
+                       }
+                       creds = insecure.NewCredentials()
+                       target = "unix:" + uri.Path
+               default:
+                       return nil, adbc.Error{Msg: fmt.Sprintf("Invalid URI 
'%s': unrecognized transport '%s' (expected 'tls', 'tcp', or 'unix')", loc, 
transport), Code: adbc.StatusInvalidArgument}
+               }
        }
 
        dv, _ := d.DriverInfo.GetInfoForInfoCode(adbc.InfoDriverVersion)
diff --git a/go/adbc/driver/flightsql/flightsql_driver.go 
b/go/adbc/driver/flightsql/flightsql_driver.go
index 4af7ff23e..169b60a08 100644
--- a/go/adbc/driver/flightsql/flightsql_driver.go
+++ b/go/adbc/driver/flightsql/flightsql_driver.go
@@ -28,7 +28,14 @@
 //
 //     db, err := sql.Open("flightsql", "uri=<flight sql db url>")
 //
-// The URI passed *must* contain a scheme, most likely "grpc+tcp://"
+// The URI passed *must* contain a scheme, most likely "flightsql://"
+// (secure TLS by default; add "?transport=tcp" for plaintext or
+// "?transport=unix" for a Unix domain socket, whose path is always
+// insecure/plaintext). The "transport" value is matched
+// case-insensitively, and an unrecognized value is rejected rather
+// than silently falling back to a default. The legacy "grpc://",
+// "grpc+tcp://", "grpc+tls://", and "grpc+unix://" schemes are also
+// still accepted.
 package flightsql
 
 import (
diff --git a/go/adbc/sqldriver/flightsql/README.md 
b/go/adbc/sqldriver/flightsql/README.md
index a9d3579f1..4a659cc19 100644
--- a/go/adbc/sqldriver/flightsql/README.md
+++ b/go/adbc/sqldriver/flightsql/README.md
@@ -49,7 +49,7 @@ import (
 )
 
 func main() {
-       db, err := sql.Open("flightsql", "uri=grpc://localhost:12345")
+       db, err := sql.Open("flightsql", 
"uri=flightsql://localhost:12345?transport=tcp")
        if err != nil {
                panic(err)
        }
diff --git a/r/adbcflightsql/README.Rmd b/r/adbcflightsql/README.Rmd
index 9f41dc585..429df3bc7 100644
--- a/r/adbcflightsql/README.Rmd
+++ b/r/adbcflightsql/README.Rmd
@@ -69,7 +69,8 @@ This is a basic example which shows you how to solve a common 
problem.
 library(adbcdrivermanager)
 
 # Use the driver manager to connect to a database. This example URI is
-# grpc://localhost:8080 and uses a Go FlightSQL/SQLite server docker image
+# flightsql://localhost:8080?transport=tcp and uses a Go FlightSQL/SQLite
+# server docker image
 uri <- Sys.getenv("ADBC_FLIGHTSQL_TEST_URI")
 db <- adbc_database_init(adbcflightsql::adbcflightsql(), uri = uri)
 con <- adbc_connection_init(db)
diff --git a/r/adbcflightsql/README.md b/r/adbcflightsql/README.md
index 751d16eeb..784579b62 100644
--- a/r/adbcflightsql/README.md
+++ b/r/adbcflightsql/README.md
@@ -59,7 +59,8 @@ This is a basic example which shows you how to solve a common 
problem.
 library(adbcdrivermanager)
 
 # Use the driver manager to connect to a database. This example URI is
-# grpc://localhost:8080 and uses a Go FlightSQL/SQLite server docker image
+# flightsql://localhost:8080?transport=tcp and uses a Go FlightSQL/SQLite
+# server docker image
 uri <- Sys.getenv("ADBC_FLIGHTSQL_TEST_URI")
 db <- adbc_database_init(adbcflightsql::adbcflightsql(), uri = uri)
 con <- adbc_connection_init(db)

Reply via email to