From ef91207de56dbedbccafac4f75a30cbad7949ab3 Mon Sep 17 00:00:00 2001 From: Marin Nozhchev Date: Sun, 28 Sep 2025 10:14:53 +0300 Subject: [PATCH 1/9] feat: basic airtable provider for go-mysql-server --- .idea/.gitignore | 8 ++++ go.go | 71 ++++++++++++++++++++++++++++- go.mod | 27 +++++++++++ go.sum | 69 ++++++++++++++++++++++++++++ provider/airdb.go | 105 +++++++++++++++++++++++++++++++++++++++++++ provider/provider.go | 37 +++++++++++++++ 6 files changed, 316 insertions(+), 1 deletion(-) create mode 100644 .idea/.gitignore create mode 100644 provider/airdb.go create mode 100644 provider/provider.go diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/go.go b/go.go index 643f397..f356a8d 100644 --- a/go.go +++ b/go.go @@ -1,5 +1,74 @@ package template +import ( + "context" + "time" + + "github.com/dolthub/vitess/go/vt/proto/query" + "github.com/mehanizm/airtable" + "github.com/sclgo/template/provider" + + sqle "github.com/dolthub/go-mysql-server" + "github.com/dolthub/go-mysql-server/memory" + "github.com/dolthub/go-mysql-server/sql" + "github.com/dolthub/go-mysql-server/sql/types" +) + +// This is an example of how to implement a MySQL server. +// After running the example, you may connect to it using the following: +// +// > mysql --host=localhost --port=3306 --user=root mydb --execute="SELECT * FROM mytable;" +// +----------+-------------------+-------------------------------+----------------------------+ +// | name | email | phone_numbers | created_at | +// +----------+-------------------+-------------------------------+----------------------------+ +// | Jane Deo | janedeo@gmail.com | ["556-565-566","777-777-777"] | 2022-11-01 12:00:00.000001 | +// | Jane Doe | jane@doe.com | [] | 2022-11-01 12:00:00.000001 | +// | John Doe | john@doe.com | ["555-555-555"] | 2022-11-01 12:00:00.000001 | +// | John Doe | johnalt@doe.com | [] | 2022-11-01 12:00:00.000001 | +// +----------+-------------------+-------------------------------+----------------------------+ +// +// The included MySQL client is used in this example, however any MySQL-compatible client will work. + +var ( + dbName = "mydb" + tableName = "mytable" + address = "localhost" + port = 3306 +) + func hello() string { - return "hello world" + //pro := createTestDatabase() + pro := provider.New(airtable.NewClient("")) + _ = sqle.NewDefault(pro) + + session := memory.NewSession(sql.NewBaseSession(), pro) + ctx := sql.NewContext(context.Background(), sql.WithSession(session)) + ctx.SetCurrentDatabase(dbName) + + return "world" +} + +func createTestDatabase() *memory.DbProvider { + db := memory.NewDatabase(dbName) + db.BaseDatabase.EnablePrimaryKeyIndexes() + + pro := memory.NewDBProvider(db) + session := memory.NewSession(sql.NewBaseSession(), pro) + ctx := sql.NewContext(context.Background(), sql.WithSession(session)) + + table := memory.NewTable(db, tableName, sql.NewPrimaryKeySchema(sql.Schema{ + {Name: "name", Type: types.Text, Nullable: false, Source: tableName, PrimaryKey: true}, + {Name: "email", Type: types.Text, Nullable: false, Source: tableName, PrimaryKey: true}, + {Name: "phone_numbers", Type: types.JSON, Nullable: false, Source: tableName}, + {Name: "created_at", Type: types.MustCreateDatetimeType(query.Type_DATETIME, 6), Nullable: false, Source: tableName}, + }), db.GetForeignKeyCollection()) + db.AddTable(tableName, table) + + creationTime := time.Unix(0, 1667304000000001000).UTC() + _ = table.Insert(ctx, sql.NewRow("Jane Deo", "janedeo@gmail.com", types.MustJSON(`["556-565-566", "777-777-777"]`), creationTime)) + _ = table.Insert(ctx, sql.NewRow("Jane Doe", "jane@doe.com", types.MustJSON(`[]`), creationTime)) + _ = table.Insert(ctx, sql.NewRow("John Doe", "john@doe.com", types.MustJSON(`["555-555-555"]`), creationTime)) + _ = table.Insert(ctx, sql.NewRow("John Doe", "johnalt@doe.com", types.MustJSON(`[]`), creationTime)) + + return pro } diff --git a/go.mod b/go.mod index 627d7a6..fdf9453 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,34 @@ go 1.24.1 require github.com/stretchr/testify v1.10.0 require ( + github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect + github.com/dolthub/flatbuffers/v23 v23.3.3-dh.2 // indirect + github.com/dolthub/go-icu-regex v0.0.0-20250327004329-6799764f2dad // indirect + github.com/dolthub/go-mysql-server v0.20.0 // indirect + github.com/dolthub/jsonpath v0.0.2-0.20240227200619-19675ab05c71 // indirect + github.com/dolthub/vitess v0.0.0-20250512224608-8fb9c6ea092c // indirect + github.com/golang/protobuf v1.5.2 // indirect + github.com/google/uuid v1.3.0 // indirect + github.com/hashicorp/golang-lru v0.5.4 // indirect + github.com/lestrrat-go/strftime v1.0.4 // indirect + github.com/mehanizm/airtable v0.3.4 // indirect + github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/shopspring/decimal v1.3.1 // indirect + github.com/sirupsen/logrus v1.8.1 // indirect + github.com/tetratelabs/wazero v1.8.2 // indirect + go.opentelemetry.io/otel v1.31.0 // indirect + go.opentelemetry.io/otel/trace v1.31.0 // indirect + golang.org/x/mod v0.12.0 // indirect + golang.org/x/sync v0.3.0 // indirect + golang.org/x/sys v0.12.0 // indirect + golang.org/x/text v0.6.0 // indirect + golang.org/x/time v0.8.0 // indirect + golang.org/x/tools v0.13.0 // indirect + google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f // indirect + google.golang.org/grpc v1.53.0 // indirect + google.golang.org/protobuf v1.28.1 // indirect + gopkg.in/src-d/go-errors.v1 v1.0.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 713a0b4..14ac40e 100644 --- a/go.sum +++ b/go.sum @@ -1,10 +1,79 @@ +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dolthub/flatbuffers/v23 v23.3.3-dh.2 h1:u3PMzfF8RkKd3lB9pZ2bfn0qEG+1Gms9599cr0REMww= +github.com/dolthub/flatbuffers/v23 v23.3.3-dh.2/go.mod h1:mIEZOHnFx4ZMQeawhw9rhsj+0zwQj7adVsnBX7t+eKY= +github.com/dolthub/go-icu-regex v0.0.0-20250327004329-6799764f2dad h1:66ZPawHszNu37VPQckdhX1BPPVzREsGgNxQeefnlm3g= +github.com/dolthub/go-icu-regex v0.0.0-20250327004329-6799764f2dad/go.mod h1:ylU4XjUpsMcvl/BKeRRMXSH7e7WBrPXdSLvnRJYrxEA= +github.com/dolthub/go-mysql-server v0.20.0 h1:oB1WXD5TwdjhdyJDbF6VgVxyEbCevDRok9yEXefpoyI= +github.com/dolthub/go-mysql-server v0.20.0/go.mod h1:5ZdrW0fHZbz+8CngT9gksqSX4H3y+7v1pns7tJCEpu0= +github.com/dolthub/jsonpath v0.0.2-0.20240227200619-19675ab05c71 h1:bMGS25NWAGTEtT5tOBsCuCrlYnLRKpbJVJkDbrTRhwQ= +github.com/dolthub/jsonpath v0.0.2-0.20240227200619-19675ab05c71/go.mod h1:2/2zjLQ/JOOSbbSboojeg+cAwcRV0fDLzIiWch/lhqI= +github.com/dolthub/vitess v0.0.0-20250512224608-8fb9c6ea092c h1:imdag6PPCHAO2rZNsFoQoR4I/vIVTmO/czoOl5rUnbk= +github.com/dolthub/vitess v0.0.0-20250512224608-8fb9c6ea092c/go.mod h1:1gQZs/byeHLMSul3Lvl3MzioMtOW1je79QYGyi2fd70= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= +github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc/go.mod h1:kopuH9ugFRkIXf3YoqHKyrJ9YfUFsckUU9S7B+XP+is= +github.com/lestrrat-go/strftime v1.0.4 h1:T1Rb9EPkAhgxKqbcMIPguPq8glqXTA1koF8n9BHElA8= +github.com/lestrrat-go/strftime v1.0.4/go.mod h1:E1nN3pCbtMSu1yjSVeyuRFVm/U0xoR76fd03sz+Qz4g= +github.com/mehanizm/airtable v0.3.4 h1:2ny8QN+O2YIs0rBXn61OAUlsBXaLDPsBhVILeWZBBNo= +github.com/mehanizm/airtable v0.3.4/go.mod h1:ucwKW2iPJoEK9dIL7ueCaDdjClpG6pplAOGabgJtoLg= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8= +github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= +github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= +github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/tetratelabs/wazero v1.8.2 h1:yIgLR/b2bN31bjxwXHD8a3d+BogigR952csSDdLYEv4= +github.com/tetratelabs/wazero v1.8.2/go.mod h1:yAI0XTsMBhREkM/YDAK/zNou3GoiAce1P6+rp/wQhjs= +go.opentelemetry.io/otel v1.31.0 h1:NsJcKPIW0D0H3NgzPDHmo0WW6SptzPdqg/L1zsIm2hY= +go.opentelemetry.io/otel v1.31.0/go.mod h1:O0C14Yl9FgkjqcCZAsE053C13OaddMYr/hz6clDkEJE= +go.opentelemetry.io/otel/trace v1.31.0 h1:ffjsj1aRouKewfr85U2aGagJ46+MvodynlQ1HYdmJys= +go.opentelemetry.io/otel/trace v1.31.0/go.mod h1:TXZkRk7SM2ZQLtR6eoAWQFIHPvzQ06FJAsO1tJg480A= +golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= +golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= +golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/text v0.6.0 h1:3XmdazWV+ubf7QgHSTWeykHOci5oeekaGJBLkrkaw4k= +golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg= +golang.org/x/time v0.8.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ= +golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f h1:BWUVssLB0HVOSY78gIdvk1dTVYtT1y8SBWtPYuTJ/6w= +google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/grpc v1.53.0 h1:LAv2ds7cmFV/XTS3XG1NneeENYrXGmorPxsBbptIjNc= +google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= +google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b h1:QRR6H1YWRnHb4Y/HeNFCTJLFVxaq6wH4YuVdsUOr75U= +gopkg.in/src-d/go-errors.v1 v1.0.0 h1:cooGdZnCjYbeS1zb1s6pVAAimTdKceRrpn7aKOnNIfc= +gopkg.in/src-d/go-errors.v1 v1.0.0/go.mod h1:q1cBlomlw2FnDBDNGlnh6X0jPihy+QxZfMMNxPCbdYg= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/provider/airdb.go b/provider/airdb.go new file mode 100644 index 0000000..fdc837a --- /dev/null +++ b/provider/airdb.go @@ -0,0 +1,105 @@ +package provider + +import ( + "strings" + + "github.com/dolthub/go-mysql-server/sql" + "github.com/dolthub/go-mysql-server/sql/types" + "github.com/mehanizm/airtable" +) + +var _ sql.Database = (*AirDB)(nil) +var _ sql.Table = (*AirTable)(nil) + +type AirDB struct { + client *airtable.Client + name string + schema *airtable.BaseConfig +} + +func (a *AirDB) Name() string { + return a.name +} + +func (a *AirDB) GetTableInsensitive(ctx *sql.Context, tblName string) (sql.Table, bool, error) { + tbls, err := a.schema.GetTablesContext(ctx) + if err != nil { + return nil, false, err + } + for _, tbl := range tbls.Tables { + if strings.ToLower(tblName) == strings.ToLower(tbl.Name) { + return AirTable{ + airSchema: *tbl, + schema: toSqlSchema(tbl), + }, true, nil + } + } + return nil, false, nil +} + +func toSqlSchema(tbl *airtable.TableSchema) sql.Schema { + schema := sql.Schema{} + for _, field := range tbl.Fields { + schema = append(schema, &sql.Column{ + Name: field.Name, + Type: toSqlType(field.Type), + Comment: field.Description, + }) + } + return schema +} + +func toSqlType(_ string) sql.Type { + return types.Text +} + +func (a *AirDB) GetTableNames(ctx *sql.Context) ([]string, error) { + tbls, err := a.schema.GetTablesContext(ctx) + if err != nil { + return nil, err + } + names := make([]string, 0) + for _, tbl := range tbls.Tables { + names = append(names, tbl.Name) + } + return names, nil +} + +func NewAirDB(name string, client *airtable.Client) *AirDB { + return &AirDB{ + client: client, + name: name, + schema: client.GetBaseSchema(name), + } +} + +type AirTable struct { + airSchema airtable.TableSchema + schema sql.Schema +} + +func (a AirTable) Name() string { + return a.airSchema.Name +} + +func (a AirTable) String() string { + return a.Name() +} + +func (a AirTable) Schema() sql.Schema { + return a.schema +} + +func (a AirTable) Collation() sql.CollationID { + return sql.Collation_binary +} + +func (a AirTable) Partitions(context *sql.Context) (sql.PartitionIter, error) { + //TODO implement me + panic("implement me") +} + +func (a AirTable) PartitionRows(context *sql.Context, partition sql.Partition) (sql.RowIter, error) { + //TODO implement me + panic("implement me") +} diff --git a/provider/provider.go b/provider/provider.go new file mode 100644 index 0000000..86340d8 --- /dev/null +++ b/provider/provider.go @@ -0,0 +1,37 @@ +package provider + +import ( + "github.com/dolthub/go-mysql-server/sql" + "github.com/mehanizm/airtable" +) + +var _ sql.DatabaseProvider = (*AirProvider)(nil) + +type AirProvider struct { + client *airtable.Client +} + +func (a *AirProvider) Database(ctx *sql.Context, name string) (sql.Database, error) { + return NewAirDB(name, a.client), nil +} + +func (a *AirProvider) HasDatabase(ctx *sql.Context, name string) bool { + return true +} + +func (a *AirProvider) AllDatabases(ctx *sql.Context) []sql.Database { + cfg := a.client.GetBases() + bases, err := cfg.DoContext(ctx) + if err != nil { + return nil + } + var dbs []sql.Database + for _, b := range bases.Bases { + dbs = append(dbs, NewAirDB(b.Name, a.client)) + } + return dbs +} + +func New(client *airtable.Client) *AirProvider { + return &AirProvider{client: client} +} From b4b02d6556dc870c7ef5b1be372d9ac27d85e9e8 Mon Sep 17 00:00:00 2001 From: Marin Nozhchev Date: Sun, 28 Sep 2025 10:25:39 +0300 Subject: [PATCH 2/9] fixup --- .gitignore | 4 +++- go.go | 32 +------------------------------- go.mod | 12 +++++++----- go.sum | 19 ++++++++++++++----- 4 files changed, 25 insertions(+), 42 deletions(-) diff --git a/.gitignore b/.gitignore index 05ca3b9..23186d3 100644 --- a/.gitignore +++ b/.gitignore @@ -23,4 +23,6 @@ go.work.sum # env file .env -/coverage \ No newline at end of file +/coverage + +/.idea \ No newline at end of file diff --git a/go.go b/go.go index f356a8d..d50685c 100644 --- a/go.go +++ b/go.go @@ -2,16 +2,13 @@ package template import ( "context" - "time" - "github.com/dolthub/vitess/go/vt/proto/query" "github.com/mehanizm/airtable" - "github.com/sclgo/template/provider" + "github.com/sclgo/airtable-sql/provider" sqle "github.com/dolthub/go-mysql-server" "github.com/dolthub/go-mysql-server/memory" "github.com/dolthub/go-mysql-server/sql" - "github.com/dolthub/go-mysql-server/sql/types" ) // This is an example of how to implement a MySQL server. @@ -32,8 +29,6 @@ import ( var ( dbName = "mydb" tableName = "mytable" - address = "localhost" - port = 3306 ) func hello() string { @@ -47,28 +42,3 @@ func hello() string { return "world" } - -func createTestDatabase() *memory.DbProvider { - db := memory.NewDatabase(dbName) - db.BaseDatabase.EnablePrimaryKeyIndexes() - - pro := memory.NewDBProvider(db) - session := memory.NewSession(sql.NewBaseSession(), pro) - ctx := sql.NewContext(context.Background(), sql.WithSession(session)) - - table := memory.NewTable(db, tableName, sql.NewPrimaryKeySchema(sql.Schema{ - {Name: "name", Type: types.Text, Nullable: false, Source: tableName, PrimaryKey: true}, - {Name: "email", Type: types.Text, Nullable: false, Source: tableName, PrimaryKey: true}, - {Name: "phone_numbers", Type: types.JSON, Nullable: false, Source: tableName}, - {Name: "created_at", Type: types.MustCreateDatetimeType(query.Type_DATETIME, 6), Nullable: false, Source: tableName}, - }), db.GetForeignKeyCollection()) - db.AddTable(tableName, table) - - creationTime := time.Unix(0, 1667304000000001000).UTC() - _ = table.Insert(ctx, sql.NewRow("Jane Deo", "janedeo@gmail.com", types.MustJSON(`["556-565-566", "777-777-777"]`), creationTime)) - _ = table.Insert(ctx, sql.NewRow("Jane Doe", "jane@doe.com", types.MustJSON(`[]`), creationTime)) - _ = table.Insert(ctx, sql.NewRow("John Doe", "john@doe.com", types.MustJSON(`["555-555-555"]`), creationTime)) - _ = table.Insert(ctx, sql.NewRow("John Doe", "johnalt@doe.com", types.MustJSON(`[]`), creationTime)) - - return pro -} diff --git a/go.mod b/go.mod index fdf9453..74dfcf4 100644 --- a/go.mod +++ b/go.mod @@ -1,22 +1,24 @@ -module github.com/sclgo/template +module github.com/sclgo/airtable-sql go 1.24.1 -require github.com/stretchr/testify v1.10.0 +require ( + github.com/dolthub/go-mysql-server v0.20.0 + github.com/mehanizm/airtable v0.3.4 + github.com/stretchr/testify v1.11.1 +) require ( github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/dolthub/flatbuffers/v23 v23.3.3-dh.2 // indirect github.com/dolthub/go-icu-regex v0.0.0-20250327004329-6799764f2dad // indirect - github.com/dolthub/go-mysql-server v0.20.0 // indirect github.com/dolthub/jsonpath v0.0.2-0.20240227200619-19675ab05c71 // indirect github.com/dolthub/vitess v0.0.0-20250512224608-8fb9c6ea092c // indirect github.com/golang/protobuf v1.5.2 // indirect github.com/google/uuid v1.3.0 // indirect github.com/hashicorp/golang-lru v0.5.4 // indirect github.com/lestrrat-go/strftime v1.0.4 // indirect - github.com/mehanizm/airtable v0.3.4 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/shopspring/decimal v1.3.1 // indirect @@ -32,7 +34,7 @@ require ( golang.org/x/tools v0.13.0 // indirect google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f // indirect google.golang.org/grpc v1.53.0 // indirect - google.golang.org/protobuf v1.28.1 // indirect + google.golang.org/protobuf v1.33.0 // indirect gopkg.in/src-d/go-errors.v1 v1.0.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 14ac40e..faaa8b6 100644 --- a/go.sum +++ b/go.sum @@ -17,15 +17,22 @@ github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaS github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc h1:RKf14vYWi2ttpEmkA4aQ3j4u9dStX2t4M8UM6qqNsG8= github.com/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc/go.mod h1:kopuH9ugFRkIXf3YoqHKyrJ9YfUFsckUU9S7B+XP+is= github.com/lestrrat-go/strftime v1.0.4 h1:T1Rb9EPkAhgxKqbcMIPguPq8glqXTA1koF8n9BHElA8= github.com/lestrrat-go/strftime v1.0.4/go.mod h1:E1nN3pCbtMSu1yjSVeyuRFVm/U0xoR76fd03sz+Qz4g= github.com/mehanizm/airtable v0.3.4 h1:2ny8QN+O2YIs0rBXn61OAUlsBXaLDPsBhVILeWZBBNo= github.com/mehanizm/airtable v0.3.4/go.mod h1:ucwKW2iPJoEK9dIL7ueCaDdjClpG6pplAOGabgJtoLg= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -39,8 +46,8 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= -github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/tetratelabs/wazero v1.8.2 h1:yIgLR/b2bN31bjxwXHD8a3d+BogigR952csSDdLYEv4= github.com/tetratelabs/wazero v1.8.2/go.mod h1:yAI0XTsMBhREkM/YDAK/zNou3GoiAce1P6+rp/wQhjs= go.opentelemetry.io/otel v1.31.0 h1:NsJcKPIW0D0H3NgzPDHmo0WW6SptzPdqg/L1zsIm2hY= @@ -49,6 +56,8 @@ go.opentelemetry.io/otel/trace v1.31.0 h1:ffjsj1aRouKewfr85U2aGagJ46+MvodynlQ1HY go.opentelemetry.io/otel/trace v1.31.0/go.mod h1:TXZkRk7SM2ZQLtR6eoAWQFIHPvzQ06FJAsO1tJg480A= golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8= +golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -67,11 +76,11 @@ google.golang.org/grpc v1.53.0 h1:LAv2ds7cmFV/XTS3XG1NneeENYrXGmorPxsBbptIjNc= google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= -google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= +google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b h1:QRR6H1YWRnHb4Y/HeNFCTJLFVxaq6wH4YuVdsUOr75U= +gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/src-d/go-errors.v1 v1.0.0 h1:cooGdZnCjYbeS1zb1s6pVAAimTdKceRrpn7aKOnNIfc= gopkg.in/src-d/go-errors.v1 v1.0.0/go.mod h1:q1cBlomlw2FnDBDNGlnh6X0jPihy+QxZfMMNxPCbdYg= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= From bcf188d8b53dfe4c219fed29a322ce5c7ac4eb3f Mon Sep 17 00:00:00 2001 From: Marin Nozhchev Date: Sun, 12 Oct 2025 10:19:29 +0300 Subject: [PATCH 3/9] feat: basic read from tables --- go.go | 37 ++++++++++-- go.mod | 21 ++++--- go.sum | 53 +++++++++-------- go_test.go | 4 +- internal/errhelp/merry.go | 17 ++++++ provider/airdb.go | 49 ++++------------ provider/airsqltable.go | 101 ++++++++++++++++++++++++++++++++ provider/provider.go | 26 +++++++- provider/provider_integ_test.go | 39 ++++++++++++ 9 files changed, 265 insertions(+), 82 deletions(-) create mode 100644 internal/errhelp/merry.go create mode 100644 provider/airsqltable.go create mode 100644 provider/provider_integ_test.go diff --git a/go.go b/go.go index d50685c..864af5e 100644 --- a/go.go +++ b/go.go @@ -2,8 +2,13 @@ package template import ( "context" + "errors" + "fmt" + "os" "github.com/mehanizm/airtable" + "github.com/murfffi/gorich/helperr" + "github.com/murfffi/gorich/lang" "github.com/sclgo/airtable-sql/provider" sqle "github.com/dolthub/go-mysql-server" @@ -27,18 +32,38 @@ import ( // The included MySQL client is used in this example, however any MySQL-compatible client will work. var ( - dbName = "mydb" - tableName = "mytable" + dbName = "Order Assembly" ) -func hello() string { +func hello() (string, error) { //pro := createTestDatabase() - pro := provider.New(airtable.NewClient("")) - _ = sqle.NewDefault(pro) + pro := provider.New(airtable.NewClient(os.Getenv("AIRTABLE_API_KEY"))) + engine := sqle.NewDefault(pro) session := memory.NewSession(sql.NewBaseSession(), pro) ctx := sql.NewContext(context.Background(), sql.WithSession(session)) ctx.SetCurrentDatabase(dbName) - return "world" + schema, rows, flags, err := engine.Query(ctx, `SELECT "Part Name" FROM Parts`) + if err != nil { + return "", err + } + if flags.DmlIsSet() { + return "", fmt.Errorf("unexpected flags: %v", flags) + } + if schema.IndexOfColName("table_name") < 0 { + return "", errors.New("no table_name found") + } + defer helperr.CloseQuietly(funcCloser(lang.Bind(rows.Close, ctx))) + row, err := rows.Next(ctx) + if err != nil { + return "", err + } + return fmt.Sprint(row[0]), nil +} + +type funcCloser func() error + +func (f funcCloser) Close() error { + return f() } diff --git a/go.mod b/go.mod index 74dfcf4..e859e82 100644 --- a/go.mod +++ b/go.mod @@ -3,8 +3,12 @@ module github.com/sclgo/airtable-sql go 1.24.1 require ( + github.com/ansel1/merry v1.8.1 + github.com/ansel1/merry/v2 v2.2.3 github.com/dolthub/go-mysql-server v0.20.0 github.com/mehanizm/airtable v0.3.4 + github.com/murfffi/gorich v0.3.0 + github.com/samber/lo v1.52.0 github.com/stretchr/testify v1.11.1 ) @@ -15,8 +19,7 @@ require ( github.com/dolthub/go-icu-regex v0.0.0-20250327004329-6799764f2dad // indirect github.com/dolthub/jsonpath v0.0.2-0.20240227200619-19675ab05c71 // indirect github.com/dolthub/vitess v0.0.0-20250512224608-8fb9c6ea092c // indirect - github.com/golang/protobuf v1.5.2 // indirect - github.com/google/uuid v1.3.0 // indirect + github.com/google/uuid v1.6.0 // indirect github.com/hashicorp/golang-lru v0.5.4 // indirect github.com/lestrrat-go/strftime v1.0.4 // indirect github.com/pkg/errors v0.9.1 // indirect @@ -26,15 +29,15 @@ require ( github.com/tetratelabs/wazero v1.8.2 // indirect go.opentelemetry.io/otel v1.31.0 // indirect go.opentelemetry.io/otel/trace v1.31.0 // indirect - golang.org/x/mod v0.12.0 // indirect - golang.org/x/sync v0.3.0 // indirect - golang.org/x/sys v0.12.0 // indirect - golang.org/x/text v0.6.0 // indirect + golang.org/x/mod v0.17.0 // indirect + golang.org/x/sync v0.11.0 // indirect + golang.org/x/sys v0.24.0 // indirect + golang.org/x/text v0.22.0 // indirect golang.org/x/time v0.8.0 // indirect - golang.org/x/tools v0.13.0 // indirect + golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f // indirect - google.golang.org/grpc v1.53.0 // indirect - google.golang.org/protobuf v1.33.0 // indirect + google.golang.org/grpc v1.65.0-dev // indirect + google.golang.org/protobuf v1.34.2 // indirect gopkg.in/src-d/go-errors.v1 v1.0.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index faaa8b6..74a3456 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,7 @@ +github.com/ansel1/merry v1.8.1 h1:z2o6oeJiJ7WNuBp6XAW6BQScBl6vULWxGdw5A/BHJgQ= +github.com/ansel1/merry v1.8.1/go.mod h1:wJVu1mHEtEUWq5zTTX9RiWjcE+xL8y7BGYl2VTYdP7M= +github.com/ansel1/merry/v2 v2.2.3 h1:/gBjiifpoymj+iV/8QApOET6Q4++DZJp55VR6fcHkIQ= +github.com/ansel1/merry/v2 v2.2.3/go.mod h1:Rs65Tv8RrdygaFCkV2VqLBTFe6HYIHFzEZRzvuIP0PU= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -13,14 +17,12 @@ github.com/dolthub/jsonpath v0.0.2-0.20240227200619-19675ab05c71 h1:bMGS25NWAGTE github.com/dolthub/jsonpath v0.0.2-0.20240227200619-19675ab05c71/go.mod h1:2/2zjLQ/JOOSbbSboojeg+cAwcRV0fDLzIiWch/lhqI= github.com/dolthub/vitess v0.0.0-20250512224608-8fb9c6ea092c h1:imdag6PPCHAO2rZNsFoQoR4I/vIVTmO/czoOl5rUnbk= github.com/dolthub/vitess v0.0.0-20250512224608-8fb9c6ea092c/go.mod h1:1gQZs/byeHLMSul3Lvl3MzioMtOW1je79QYGyi2fd70= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/go-errors/errors v1.5.1 h1:ZwEMSLRCapFLflTpT7NKaAc7ukJ8ZPEjzlxt8rPN8bk= +github.com/go-errors/errors v1.5.1/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= @@ -31,6 +33,8 @@ github.com/lestrrat-go/strftime v1.0.4 h1:T1Rb9EPkAhgxKqbcMIPguPq8glqXTA1koF8n9B github.com/lestrrat-go/strftime v1.0.4/go.mod h1:E1nN3pCbtMSu1yjSVeyuRFVm/U0xoR76fd03sz+Qz4g= github.com/mehanizm/airtable v0.3.4 h1:2ny8QN+O2YIs0rBXn61OAUlsBXaLDPsBhVILeWZBBNo= github.com/mehanizm/airtable v0.3.4/go.mod h1:ucwKW2iPJoEK9dIL7ueCaDdjClpG6pplAOGabgJtoLg= +github.com/murfffi/gorich v0.3.0 h1:cRsCCTD0A2eyiAjeSMwyOkjALDsVzgJO0ghY+cXsDJY= +github.com/murfffi/gorich v0.3.0/go.mod h1:fozPmSzPmc1r0xnNtk3HE6xxxlzlBqo4rlChD/iJZcM= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -38,6 +42,8 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/samber/lo v1.52.0 h1:Rvi+3BFHES3A8meP33VPAxiBZX/Aws5RxrschYGjomw= +github.com/samber/lo v1.52.0/go.mod h1:4+MXEGsJzbKGaUEQFKBq2xtfuznW9oz/WrgyzMzRoM0= github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8= github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= @@ -54,30 +60,27 @@ go.opentelemetry.io/otel v1.31.0 h1:NsJcKPIW0D0H3NgzPDHmo0WW6SptzPdqg/L1zsIm2hY= go.opentelemetry.io/otel v1.31.0/go.mod h1:O0C14Yl9FgkjqcCZAsE053C13OaddMYr/hz6clDkEJE= go.opentelemetry.io/otel/trace v1.31.0 h1:ffjsj1aRouKewfr85U2aGagJ46+MvodynlQ1HYdmJys= go.opentelemetry.io/otel/trace v1.31.0/go.mod h1:TXZkRk7SM2ZQLtR6eoAWQFIHPvzQ06FJAsO1tJg480A= -golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= -golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8= -golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= -golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= -golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= +golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= +golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w= +golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= -golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/text v0.6.0 h1:3XmdazWV+ubf7QgHSTWeykHOci5oeekaGJBLkrkaw4k= -golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= +golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= +golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg= golang.org/x/time v0.8.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= -golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ= -golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f h1:BWUVssLB0HVOSY78gIdvk1dTVYtT1y8SBWtPYuTJ/6w= google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/grpc v1.53.0 h1:LAv2ds7cmFV/XTS3XG1NneeENYrXGmorPxsBbptIjNc= -google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= -google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/grpc v1.65.0-dev h1:aHJBb+Hz0geqgMFGwU6fuHrgXrIq2/vO7kwpjfJMPzk= +google.golang.org/grpc v1.65.0-dev/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg= +google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= +google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b h1:QRR6H1YWRnHb4Y/HeNFCTJLFVxaq6wH4YuVdsUOr75U= gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/go_test.go b/go_test.go index e29f9c7..329f9d7 100644 --- a/go_test.go +++ b/go_test.go @@ -7,5 +7,7 @@ import ( ) func TestHello(t *testing.T) { - require.Equal(t, "hello world", hello()) + res, err := hello() + require.NoError(t, err) + require.Equal(t, "world", res) } diff --git a/internal/errhelp/merry.go b/internal/errhelp/merry.go new file mode 100644 index 0000000..4f62b30 --- /dev/null +++ b/internal/errhelp/merry.go @@ -0,0 +1,17 @@ +package errhelp + +import ( + "fmt" + + "github.com/ansel1/merry/v2" +) + +func Errorf(format string, a ...any) error { + return merry.Wrap(fmt.Errorf(format, a...)) +} + +type FuncCloser func() error + +func (f FuncCloser) Close() error { + return f() +} diff --git a/provider/airdb.go b/provider/airdb.go index fdc837a..e8ab48d 100644 --- a/provider/airdb.go +++ b/provider/airdb.go @@ -3,16 +3,18 @@ package provider import ( "strings" + "github.com/ansel1/merry/v2" "github.com/dolthub/go-mysql-server/sql" "github.com/dolthub/go-mysql-server/sql/types" "github.com/mehanizm/airtable" ) var _ sql.Database = (*AirDB)(nil) -var _ sql.Table = (*AirTable)(nil) +var _ sql.Table = (*AirSqlTable)(nil) type AirDB struct { client *airtable.Client + dbId string name string schema *airtable.BaseConfig } @@ -28,9 +30,10 @@ func (a *AirDB) GetTableInsensitive(ctx *sql.Context, tblName string) (sql.Table } for _, tbl := range tbls.Tables { if strings.ToLower(tblName) == strings.ToLower(tbl.Name) { - return AirTable{ - airSchema: *tbl, - schema: toSqlSchema(tbl), + return AirSqlTable{ + tableClient: a.client.GetTable(a.dbId, tblName), + airSchema: tbl, + schema: toSqlSchema(tbl), }, true, nil } } @@ -56,7 +59,7 @@ func toSqlType(_ string) sql.Type { func (a *AirDB) GetTableNames(ctx *sql.Context) ([]string, error) { tbls, err := a.schema.GetTablesContext(ctx) if err != nil { - return nil, err + return nil, merry.Wrap(err) } names := make([]string, 0) for _, tbl := range tbls.Tables { @@ -65,41 +68,11 @@ func (a *AirDB) GetTableNames(ctx *sql.Context) ([]string, error) { return names, nil } -func NewAirDB(name string, client *airtable.Client) *AirDB { +func NewAirDB(name string, dbId string, client *airtable.Client) *AirDB { return &AirDB{ client: client, + dbId: dbId, name: name, - schema: client.GetBaseSchema(name), + schema: client.GetBaseSchema(dbId), } } - -type AirTable struct { - airSchema airtable.TableSchema - schema sql.Schema -} - -func (a AirTable) Name() string { - return a.airSchema.Name -} - -func (a AirTable) String() string { - return a.Name() -} - -func (a AirTable) Schema() sql.Schema { - return a.schema -} - -func (a AirTable) Collation() sql.CollationID { - return sql.Collation_binary -} - -func (a AirTable) Partitions(context *sql.Context) (sql.PartitionIter, error) { - //TODO implement me - panic("implement me") -} - -func (a AirTable) PartitionRows(context *sql.Context, partition sql.Partition) (sql.RowIter, error) { - //TODO implement me - panic("implement me") -} diff --git a/provider/airsqltable.go b/provider/airsqltable.go new file mode 100644 index 0000000..c389359 --- /dev/null +++ b/provider/airsqltable.go @@ -0,0 +1,101 @@ +package provider + +import ( + "io" + + "github.com/ansel1/merry/v2" + "github.com/dolthub/go-mysql-server/memory" + "github.com/dolthub/go-mysql-server/sql" + "github.com/mehanizm/airtable" +) + +type AirSqlTable struct { + tableClient *airtable.Table + airSchema *airtable.TableSchema + schema sql.Schema +} + +func (a AirSqlTable) Name() string { + return a.airSchema.Name +} + +func (a AirSqlTable) String() string { + return a.Name() +} + +func (a AirSqlTable) Schema() sql.Schema { + return a.schema +} + +func (a AirSqlTable) Collation() sql.CollationID { + return sql.Collation_binary +} + +func (a AirSqlTable) Partitions(*sql.Context) (sql.PartitionIter, error) { + return &slicePartitionIter{ + partitions: []sql.Partition{ + &memory.Partition{}, + }, + idx: 0, + }, nil +} + +func (a AirSqlTable) PartitionRows(ctx *sql.Context, _ sql.Partition) (sql.RowIter, error) { + recordsCfg := a.tableClient.GetRecords() + recs, err := recordsCfg.DoContext(ctx) + if err != nil { + return nil, merry.Wrap(err) + } + return &basicRowIter{ + recs: recs, + airSchema: a.airSchema, + idx: 0, + }, nil + +} + +type slicePartitionIter struct { + partitions []sql.Partition + idx int +} + +func (i *slicePartitionIter) Next(*sql.Context) (sql.Partition, error) { + if i.idx >= len(i.partitions) { + return nil, io.EOF + } + i.idx++ + return i.partitions[i.idx-1], nil +} +func (i *slicePartitionIter) Close(*sql.Context) error { + i.partitions = nil + return nil +} + +type basicRowIter struct { + recs *airtable.Records + airSchema *airtable.TableSchema + idx int +} + +func (b *basicRowIter) Dispose() { + b.recs = nil +} + +func (b *basicRowIter) Next(*sql.Context) (sql.Row, error) { + if b.idx >= len(b.recs.Records) { + return nil, io.EOF + } + rec := b.recs.Records[b.idx].Fields + b.idx++ + + var values []any + for _, field := range b.airSchema.Fields { + values = append(values, rec[field.Name]) + } + return values, nil +} + +func (b *basicRowIter) Close(*sql.Context) error { + b.recs = nil + return nil +} diff --git a/provider/provider.go b/provider/provider.go index 86340d8..b900561 100644 --- a/provider/provider.go +++ b/provider/provider.go @@ -1,8 +1,13 @@ package provider import ( + "slices" + + "github.com/ansel1/merry" "github.com/dolthub/go-mysql-server/sql" "github.com/mehanizm/airtable" + "github.com/samber/lo/it" + "github.com/sclgo/airtable-sql/internal/errhelp" ) var _ sql.DatabaseProvider = (*AirProvider)(nil) @@ -12,11 +17,26 @@ type AirProvider struct { } func (a *AirProvider) Database(ctx *sql.Context, name string) (sql.Database, error) { - return NewAirDB(name, a.client), nil + cfg := a.client.GetBases() + bases, err := cfg.DoContext(ctx) + if err != nil { + return nil, errhelp.Errorf("could list bases while looking for %s: %w", name, err) + } + id := "" + for _, b := range bases.Bases { + if b.Name == name { + id = b.ID + } + } + if id == "" { + return nil, merry.New("could not find base for " + name) + } + return NewAirDB(name, id, a.client), nil } func (a *AirProvider) HasDatabase(ctx *sql.Context, name string) bool { - return true + allDbs := a.AllDatabases(ctx) + return it.Contains(it.Map(slices.Values(allDbs), sql.Database.Name), name) } func (a *AirProvider) AllDatabases(ctx *sql.Context) []sql.Database { @@ -27,7 +47,7 @@ func (a *AirProvider) AllDatabases(ctx *sql.Context) []sql.Database { } var dbs []sql.Database for _, b := range bases.Bases { - dbs = append(dbs, NewAirDB(b.Name, a.client)) + dbs = append(dbs, NewAirDB(b.Name, b.ID, a.client)) } return dbs } diff --git a/provider/provider_integ_test.go b/provider/provider_integ_test.go new file mode 100644 index 0000000..729ed13 --- /dev/null +++ b/provider/provider_integ_test.go @@ -0,0 +1,39 @@ +package provider_test + +import ( + "os" + "testing" + + sqle "github.com/dolthub/go-mysql-server" + "github.com/dolthub/go-mysql-server/memory" + "github.com/dolthub/go-mysql-server/sql" + "github.com/mehanizm/airtable" + "github.com/murfffi/gorich/helperr" + "github.com/murfffi/gorich/lang" + "github.com/sclgo/airtable-sql/internal/errhelp" + "github.com/sclgo/airtable-sql/provider" + "github.com/stretchr/testify/require" +) + +func TestProvider(t *testing.T) { + t.Run("happy", func(t *testing.T) { + apiKey := os.Getenv("AIRTABLE_API_KEY") + require.NotEmpty(t, apiKey) + pro := provider.New(airtable.NewClient(apiKey)) + engine := sqle.NewDefault(pro) + + session := memory.NewSession(sql.NewBaseSession(), pro) + ctx := sql.NewContext(t.Context(), sql.WithSession(session)) + ctx.SetCurrentDatabase("Order Assembly") + + schema, rows, flags, err := engine.Query(ctx, "SELECT `Part Name` FROM Parts") + require.NoError(t, err) + require.False(t, flags.DmlIsSet()) + require.GreaterOrEqual(t, schema.IndexOfColName("Part Name"), 0) + defer helperr.CloseQuietly(errhelp.FuncCloser(lang.Bind(rows.Close, ctx))) + row, err := rows.Next(ctx) + require.NoError(t, err) + t.Log("row:", row) + require.Equal(t, "Power Supply Unit", row[0]) + }) +} From 6d1ee971d6a71e5c91065443e2ebaa23ca482f3a Mon Sep 17 00:00:00 2001 From: Marin Nozhchev Date: Sun, 12 Oct 2025 10:26:24 +0300 Subject: [PATCH 4/9] fixup --- provider/airdb.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/provider/airdb.go b/provider/airdb.go index e8ab48d..6ad6fb1 100644 --- a/provider/airdb.go +++ b/provider/airdb.go @@ -29,7 +29,7 @@ func (a *AirDB) GetTableInsensitive(ctx *sql.Context, tblName string) (sql.Table return nil, false, err } for _, tbl := range tbls.Tables { - if strings.ToLower(tblName) == strings.ToLower(tbl.Name) { + if strings.EqualFold(tblName, tbl.Name) { return AirSqlTable{ tableClient: a.client.GetTable(a.dbId, tblName), airSchema: tbl, From efacd4bcf9883eb4daffcccb4c3c60f5407f5046 Mon Sep 17 00:00:00 2001 From: Marin Nozhchev Date: Sun, 12 Oct 2025 10:37:02 +0300 Subject: [PATCH 5/9] Update go.go Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- go.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/go.go b/go.go index 864af5e..678de81 100644 --- a/go.go +++ b/go.go @@ -51,9 +51,9 @@ func hello() (string, error) { if flags.DmlIsSet() { return "", fmt.Errorf("unexpected flags: %v", flags) } - if schema.IndexOfColName("table_name") < 0 { - return "", errors.New("no table_name found") - } + if schema.IndexOfColName("Part Name") < 0 { + return "", errors.New("no 'Part Name' column found") + } defer helperr.CloseQuietly(funcCloser(lang.Bind(rows.Close, ctx))) row, err := rows.Next(ctx) if err != nil { From 6f302721066d7b7364059ca096b31647f0dcf9b6 Mon Sep 17 00:00:00 2001 From: Marin Nozhchev Date: Sun, 12 Oct 2025 10:38:38 +0300 Subject: [PATCH 6/9] fixup --- go_test.go | 13 ------------- 1 file changed, 13 deletions(-) delete mode 100644 go_test.go diff --git a/go_test.go b/go_test.go deleted file mode 100644 index 329f9d7..0000000 --- a/go_test.go +++ /dev/null @@ -1,13 +0,0 @@ -package template - -import ( - "testing" - - "github.com/stretchr/testify/require" -) - -func TestHello(t *testing.T) { - res, err := hello() - require.NoError(t, err) - require.Equal(t, "world", res) -} From 973bdfaaeb168fdf921ae9e34c5cc9a6c0703b40 Mon Sep 17 00:00:00 2001 From: Marin Nozhchev Date: Sun, 12 Oct 2025 10:39:59 +0300 Subject: [PATCH 7/9] Update provider/airdb.go Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- provider/airdb.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/provider/airdb.go b/provider/airdb.go index 6ad6fb1..7106989 100644 --- a/provider/airdb.go +++ b/provider/airdb.go @@ -31,7 +31,7 @@ func (a *AirDB) GetTableInsensitive(ctx *sql.Context, tblName string) (sql.Table for _, tbl := range tbls.Tables { if strings.EqualFold(tblName, tbl.Name) { return AirSqlTable{ - tableClient: a.client.GetTable(a.dbId, tblName), + tableClient: a.client.GetTable(a.dbId, tbl.Name), airSchema: tbl, schema: toSqlSchema(tbl), }, true, nil From 2d022563c7e6a3e92774d09e6a0ee3445fb39592 Mon Sep 17 00:00:00 2001 From: Marin Nozhchev Date: Sun, 12 Oct 2025 10:42:32 +0300 Subject: [PATCH 8/9] fixup --- provider/airsqltable.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/provider/airsqltable.go b/provider/airsqltable.go index c389359..749191e 100644 --- a/provider/airsqltable.go +++ b/provider/airsqltable.go @@ -77,6 +77,7 @@ type basicRowIter struct { idx int } +// Dispose implements sql.Disposable func (b *basicRowIter) Dispose() { b.recs = nil } @@ -99,3 +100,8 @@ func (b *basicRowIter) Close(*sql.Context) error { b.recs = nil return nil } + +var ( + _ sql.RowIter = (*basicRowIter)(nil) + _ sql.Disposable = (*basicRowIter)(nil) +) From f95b2279b902fd9071c9ed61d24d9f91fe98070f Mon Sep 17 00:00:00 2001 From: Marin Nozhchev Date: Sun, 12 Oct 2025 10:44:04 +0300 Subject: [PATCH 9/9] fixup --- go.go | 69 ----------------------------------------------------------- 1 file changed, 69 deletions(-) delete mode 100644 go.go diff --git a/go.go b/go.go deleted file mode 100644 index 678de81..0000000 --- a/go.go +++ /dev/null @@ -1,69 +0,0 @@ -package template - -import ( - "context" - "errors" - "fmt" - "os" - - "github.com/mehanizm/airtable" - "github.com/murfffi/gorich/helperr" - "github.com/murfffi/gorich/lang" - "github.com/sclgo/airtable-sql/provider" - - sqle "github.com/dolthub/go-mysql-server" - "github.com/dolthub/go-mysql-server/memory" - "github.com/dolthub/go-mysql-server/sql" -) - -// This is an example of how to implement a MySQL server. -// After running the example, you may connect to it using the following: -// -// > mysql --host=localhost --port=3306 --user=root mydb --execute="SELECT * FROM mytable;" -// +----------+-------------------+-------------------------------+----------------------------+ -// | name | email | phone_numbers | created_at | -// +----------+-------------------+-------------------------------+----------------------------+ -// | Jane Deo | janedeo@gmail.com | ["556-565-566","777-777-777"] | 2022-11-01 12:00:00.000001 | -// | Jane Doe | jane@doe.com | [] | 2022-11-01 12:00:00.000001 | -// | John Doe | john@doe.com | ["555-555-555"] | 2022-11-01 12:00:00.000001 | -// | John Doe | johnalt@doe.com | [] | 2022-11-01 12:00:00.000001 | -// +----------+-------------------+-------------------------------+----------------------------+ -// -// The included MySQL client is used in this example, however any MySQL-compatible client will work. - -var ( - dbName = "Order Assembly" -) - -func hello() (string, error) { - //pro := createTestDatabase() - pro := provider.New(airtable.NewClient(os.Getenv("AIRTABLE_API_KEY"))) - engine := sqle.NewDefault(pro) - - session := memory.NewSession(sql.NewBaseSession(), pro) - ctx := sql.NewContext(context.Background(), sql.WithSession(session)) - ctx.SetCurrentDatabase(dbName) - - schema, rows, flags, err := engine.Query(ctx, `SELECT "Part Name" FROM Parts`) - if err != nil { - return "", err - } - if flags.DmlIsSet() { - return "", fmt.Errorf("unexpected flags: %v", flags) - } - if schema.IndexOfColName("Part Name") < 0 { - return "", errors.New("no 'Part Name' column found") - } - defer helperr.CloseQuietly(funcCloser(lang.Bind(rows.Close, ctx))) - row, err := rows.Next(ctx) - if err != nil { - return "", err - } - return fmt.Sprint(row[0]), nil -} - -type funcCloser func() error - -func (f funcCloser) Close() error { - return f() -}