Skip to content

Commit 7d4a28a

Browse files
authored
Add support for running plugins in-process. Closes #383
1 parent bf341d2 commit 7d4a28a

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+3832
-3126
lines changed

Dockerfile

+26-21
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,24 @@
22
#
33
# Dockerfile for cross-compiling for Linux on MacOS
44
# Build the image with:
5-
# docker build --pull -f Dockerfile -t steampipe_fdw_builder:13 .
5+
# docker build --pull -f Dockerfile -t steampipe_fdw_builder:15 --build-arg="pg_version=15" .
66
#
77
# Run with:
8-
# docker run -it --rm --name sp_fdw_builder_13 -v $(pwd):/tmp/ext steampipe_fdw_builder:13
8+
# docker run -it --rm --name sp_fdw_builder -v $(pwd):/tmp/ext steampipe_fdw_builder:15
99
#
1010
#####
1111

1212
FROM ubuntu:focal
1313

14+
# We know that the FDW does not compile with PG12.
15+
# Use this so that the build fails if an ARG is not passed in.
16+
# This is useful since we can use the same container definition for the SQLite builder as well
17+
ARG pg_version=12
1418
ARG go_repo="deb http://ppa.launchpad.net/longsleep/golang-backports/ubuntu bionic main"
1519
ARG pg_repo="deb http://apt.postgresql.org/pub/repos/apt/ focal-pgdg main"
16-
ENV PG_VERS=13
17-
ENV GO_VERS=1.16
20+
21+
ENV PG_VERS=$pg_version
22+
ENV GO_VERS=1.21
1823

1924
## for apt to be noninteractive
2025
ARG DEBIAN_FRONTEND=noninteractive
@@ -28,25 +33,25 @@ RUN apt-get install -y --no-install-recommends curl
2833
RUN apt-get install -y --no-install-recommends ca-certificates
2934

3035
RUN mkdir -p /etc/apt/sources.list.d \
31-
&& apt-key adv --keyserver keyserver.ubuntu.com --recv 56A3D45E \
32-
&& apt-key adv --keyserver keyserver.ubuntu.com --recv E0C56BD4 \
33-
&& echo $go_repo > /etc/apt/sources.list.d/golang.list \
34-
&& echo $pg_repo > /etc/apt/sources.list.d/pgdb.list \
35-
&& curl https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add -
36-
36+
&& apt-key adv --keyserver keyserver.ubuntu.com --recv 56A3D45E \
37+
&& apt-key adv --keyserver keyserver.ubuntu.com --recv E0C56BD4 \
38+
&& echo $go_repo > /etc/apt/sources.list.d/golang.list \
39+
&& echo $pg_repo > /etc/apt/sources.list.d/pgdb.list \
40+
&& curl https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add -
41+
3742
RUN apt-get update
3843
RUN env DEBIAN_FRONTEND=noninteractive \
39-
apt-get install -y --no-install-recommends golang-${GO_VERS} \
40-
postgresql-${PG_VERS} postgresql-server-dev-${PG_VERS} libpq-dev wget build-essential \
41-
libgcc-7-dev \
42-
locales \
43-
tzdata \
44-
git \
45-
&& rm -rf \
46-
/var/lib/apt/lists/* \
47-
/var/cache/debconf \
48-
/tmp/* \
49-
&& apt-get clean
44+
apt-get install -y --no-install-recommends golang-${GO_VERS} \
45+
postgresql-${PG_VERS} postgresql-server-dev-${PG_VERS} libpq-dev wget build-essential \
46+
libgcc-7-dev \
47+
locales \
48+
tzdata \
49+
git \
50+
&& rm -rf \
51+
/var/lib/apt/lists/* \
52+
/var/cache/debconf \
53+
/tmp/* \
54+
&& apt-get clean
5055

5156
RUN ln -s /usr/lib/go-${GO_VERS}/bin/go /usr/bin/go
5257
RUN locale-gen en_US.UTF-8

LICENSE

+201-661
Large diffs are not rendered by default.

Makefile

+20
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# Makefile
22

33
PLATFORM=$(shell uname)
4+
GETTEXT_INCLUDE=$(shell dirname $(shell dirname $(shell readlink -f $(shell which gettext))))/include
45

56
install: build
67
if test -d ~/.steampipe/db/14.2.0; then \
@@ -9,6 +10,23 @@ install: build
910
cp ./build-$(PLATFORM)/steampipe_postgres_fdw.so ~/.steampipe/db/14.2.0/postgres/lib/postgresql/; \
1011
fi
1112

13+
# build standalone
14+
standalone: validate_plugin prebuild.go
15+
@echo "Building standalone FDW for plugin: $(plugin)"
16+
go run generate/generator.go templates . $(plugin) $(plugin_github_url)
17+
go mod tidy
18+
$(MAKE) -C ./fdw clean
19+
$(MAKE) -C ./fdw go
20+
$(MAKE) -C ./fdw
21+
$(MAKE) -C ./fdw standalone
22+
23+
rm -f prebuild.go
24+
25+
validate_plugin:
26+
ifndef plugin
27+
$(error "You must specify the 'plugin' variable")
28+
endif
29+
1230
build: prebuild.go
1331
$(MAKE) -C ./fdw clean
1432
$(MAKE) -C ./fdw go
@@ -31,6 +49,7 @@ prebuild.go:
3149
sed -i.bak 's|INTERNAL_INCLUDE_PLACEHOLDER|$(shell pg_config --includedir)|' prebuild.go
3250
sed -i.bak 's|SERVER_INCLUDE_PLACEHOLDER|$(shell pg_config --includedir-server)|' prebuild.go
3351
sed -i.bak 's|DISCLAIMER|This is generated. Do not check this in to Git|' prebuild.go
52+
sed -i.bak 's|LIB_INTL_PLACEHOLDER|$(GETTEXT_INCLUDE)|' prebuild.go
3453
rm -f prebuild.go.bak
3554

3655
clean:
@@ -43,3 +62,4 @@ clean:
4362
# Usage: make release input="v1.7.2"
4463
release:
4564
./scripts/upload_arm_asset.sh $(input)
65+

README.md

+27
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,33 @@ $ make
5454

5555
This will compile the FDW (`steampipe_postgres_fdw.so`) along with the `control` and `sql` file in the `build-$PLATFORM` directory. This will install the compiled FDW into the default Steampipe installation directory (`~/.steampipe`) - if it exists.
5656

57+
### Building the FDW for a Single Plugin
58+
59+
If you want to build the FDW for a single steampipe plugin, follow these steps. This process allows you to build the Postgres Foreign Data Wrapper (FDW) specifically for one particular plugin and run it in standalone mode on any PostgreSQL database, without any reliance on Steampipe.
60+
61+
Make sure that you have the following installed in your system:
62+
1. `Postgresql v14`
63+
1. `go`
64+
1. `gcc` for Linux
65+
66+
Steps:
67+
1. Clone this repository onto your system
68+
1. Change to the cloned directory
69+
1. Run the following commands:
70+
```
71+
$ make standalone plugin="<plugin alias>"
72+
```
73+
Replace plugin alias with the alias or short name of your plugin.
74+
75+
This command will compile the FDW specifically for the chosen plugin, and the resulting binary, control file, and SQL files will be generated.
76+
77+
#### Example:
78+
79+
Suppose you want to build the FDW for a plugin with an alias `aws` from a GitHub repository located at https://github.com/turbot/steampipe-plugin-aws. You would run the following command:
80+
```
81+
$ make standalone plugin="aws"
82+
```
83+
5784
### License
5885

5986
This open source library is licensed under the [GNU Affero General Public License v3](https://opensource.org/licenses/AGPL-3.0).

fdw.go

+42-61
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,12 @@ import (
1313
"fmt"
1414
"io/ioutil"
1515
"log"
16-
"net"
17-
"net/http"
18-
"os"
1916
"time"
2017
"unsafe"
2118

2219
"github.com/hashicorp/go-hclog"
2320
"github.com/turbot/steampipe-plugin-sdk/v5/logging"
21+
"github.com/turbot/steampipe-plugin-sdk/v5/sperr"
2422
"github.com/turbot/steampipe-postgres-fdw/hub"
2523
"github.com/turbot/steampipe-postgres-fdw/types"
2624
"github.com/turbot/steampipe-postgres-fdw/version"
@@ -56,22 +54,14 @@ func init() {
5654
log.SetOutput(logger.StandardWriter(&hclog.StandardLoggerOptions{InferLevels: true}))
5755
log.SetPrefix("")
5856
log.SetFlags(0)
57+
// create hub
58+
if err := hub.CreateHub(); err != nil {
59+
panic(err)
60+
}
5961
log.Printf("[INFO] .\n******************************************************\n\n\t\tsteampipe postgres fdw init\n\n******************************************************\n")
6062
log.Printf("[INFO] Version: v%s\n", version.FdwVersion.String())
6163
log.Printf("[INFO] Log level: %s\n", level)
6264

63-
if _, found := os.LookupEnv("STEAMPIPE_FDW_PPROF"); found {
64-
log.Printf("[INFO] PROFILING!!!!")
65-
go func() {
66-
listener, err := net.Listen("tcp", "localhost:0")
67-
if err != nil {
68-
log.Println(err)
69-
return
70-
}
71-
log.Printf("[INFO] Check http://localhost:%d/debug/pprof/", listener.Addr().(*net.TCPAddr).Port)
72-
log.Println(http.Serve(listener, nil))
73-
}()
74-
}
7565
}
7666

7767
//export goFdwGetRelSize
@@ -80,10 +70,17 @@ func goFdwGetRelSize(state *C.FdwPlanState, root *C.PlannerInfo, rows *C.double,
8070

8171
log.Printf("[TRACE] goFdwGetRelSize")
8272

83-
pluginHub, err := hub.GetHub()
73+
pluginHub := hub.GetHub()
74+
75+
// get connection name
76+
connName := GetSchemaNameFromForeignTableId(types.Oid(state.foreigntableid))
77+
78+
log.Println("[TRACE] connection name:", connName)
79+
80+
serverOpts := GetForeignServerOptionsFromFTableId(types.Oid(state.foreigntableid))
81+
err := pluginHub.ProcessImportForeignSchemaOptions(serverOpts, connName)
8482
if err != nil {
85-
FdwError(err)
86-
return
83+
FdwError(sperr.WrapWithMessage(err, "failed to process options"))
8784
}
8885

8986
// reload connection config
@@ -95,15 +92,15 @@ func goFdwGetRelSize(state *C.FdwPlanState, root *C.PlannerInfo, rows *C.double,
9592
return
9693
}
9794

98-
opts := GetFTableOptions(types.Oid(state.foreigntableid))
95+
tableOpts := GetFTableOptions(types.Oid(state.foreigntableid))
9996

10097
// build columns
10198
var columns []string
10299
if state.target_list != nil {
103100
columns = CStringListToGoArray(state.target_list)
104101
}
105102

106-
result, err := pluginHub.GetRelSize(columns, nil, opts)
103+
result, err := pluginHub.GetRelSize(columns, nil, tableOpts)
107104
if err != nil {
108105
log.Println("[ERROR] pluginHub.GetRelSize")
109106
FdwError(err)
@@ -127,17 +124,12 @@ func goFdwGetPathKeys(state *C.FdwPlanState) *C.List {
127124
}()
128125

129126
log.Printf("[TRACE] goFdwGetPathKeys")
130-
pluginHub, err := hub.GetHub()
131-
if err != nil {
132-
FdwError(err)
133-
}
127+
pluginHub := hub.GetHub()
128+
134129
var result *C.List
135130
opts := GetFTableOptions(types.Oid(state.foreigntableid))
136-
ftable := C.GetForeignTable(state.foreigntableid)
137-
rel := C.RelationIdGetRelation(ftable.relid)
138-
defer C.RelationClose(rel)
139131
// get the connection name - this is the namespace (i.e. the local schema)
140-
opts["connection"] = getNamespace(rel)
132+
opts["connection"] = GetSchemaNameFromForeignTableId(types.Oid(state.foreigntableid))
141133

142134
if opts["connection"] == constants.InternalSchema || opts["connection"] == constants.LegacyCommandSchema {
143135
return result
@@ -240,12 +232,8 @@ func goFdwBeginForeignScan(node *C.ForeignScanState, eflags C.int) {
240232
quals, unhandledRestrictions := restrictionsToQuals(node, cinfos)
241233

242234
// start the plugin hub
243-
var err error
244-
pluginHub, err := hub.GetHub()
245-
if err != nil {
246-
FdwError(err)
247-
}
248235

236+
pluginHub := hub.GetHub()
249237
s := &ExecState{
250238
Rel: rel,
251239
Opts: opts,
@@ -282,7 +270,7 @@ func goFdwIterateForeignScan(node *C.ForeignScanState) *C.TupleTableSlot {
282270

283271
slot := node.ss.ss_ScanTupleSlot
284272
C.ExecClearTuple(slot)
285-
pluginHub, _ := hub.GetHub()
273+
pluginHub := hub.GetHub()
286274

287275
log.Printf("[TRACE] goFdwIterateForeignScan, table '%s' (%p)", s.Opts["table"], s.Iter)
288276
// if the iterator has not started, start
@@ -367,8 +355,8 @@ func goFdwEndForeignScan(node *C.ForeignScanState) {
367355
}
368356
}()
369357
s := GetExecState(node.fdw_state)
370-
pluginHub, _ := hub.GetHub()
371-
if s != nil && pluginHub != nil {
358+
pluginHub := hub.GetHub()
359+
if s != nil {
372360
log.Printf("[INFO] goFdwEndForeignScan, iterator: %p", s.Iter)
373361
pluginHub.EndScan(s.Iter, int64(s.State.limit))
374362
}
@@ -386,9 +374,9 @@ func goFdwAbortCallback() {
386374
}
387375
}()
388376
log.Printf("[INFO] goFdwAbortCallback")
389-
if pluginHub, err := hub.GetHub(); err == nil {
390-
pluginHub.Abort()
391-
}
377+
pluginHub := hub.GetHub()
378+
pluginHub.Abort()
379+
392380
}
393381

394382
//export goFdwImportForeignSchema
@@ -402,12 +390,7 @@ func goFdwImportForeignSchema(stmt *C.ImportForeignSchemaStmt, serverOid C.Oid)
402390

403391
log.Printf("[INFO] goFdwImportForeignSchema remote '%s' local '%s'\n", C.GoString(stmt.remote_schema), C.GoString(stmt.local_schema))
404392
// get the plugin hub,
405-
pluginHub, err := hub.GetHub()
406-
if err != nil {
407-
log.Printf("[WARN] goFdwImportForeignSchema failed: %s", err)
408-
FdwError(err)
409-
return nil
410-
}
393+
pluginHub := hub.GetHub()
411394

412395
remoteSchema := C.GoString(stmt.remote_schema)
413396
localSchema := C.GoString(stmt.local_schema)
@@ -426,6 +409,16 @@ func goFdwImportForeignSchema(stmt *C.ImportForeignSchemaStmt, serverOid C.Oid)
426409
return sql
427410
}
428411

412+
fServer := C.GetForeignServer(serverOid)
413+
serverOptions := GetForeignServerOptions(fServer)
414+
415+
log.Println("[TRACE] goFdwImportForeignSchema serverOptions:", serverOptions)
416+
417+
err := pluginHub.ProcessImportForeignSchemaOptions(serverOptions, localSchema)
418+
if err != nil {
419+
FdwError(sperr.WrapWithMessage(err, "failed to process options"))
420+
}
421+
429422
schema, err := pluginHub.GetSchema(remoteSchema, localSchema)
430423
if err != nil {
431424
log.Printf("[WARN] goFdwImportForeignSchema failed: %s", err)
@@ -462,31 +455,22 @@ func goFdwExecForeignInsert(estate *C.EState, rinfo *C.ResultRelInfo, slot *C.Tu
462455
func handleCommandInsert(rinfo *C.ResultRelInfo, slot *C.TupleTableSlot, rel C.Relation) *C.TupleTableSlot {
463456
relid := rinfo.ri_RelationDesc.rd_id
464457
opts := GetFTableOptions(types.Oid(relid))
458+
pluginHub := hub.GetHub()
465459

466460
switch opts["table"] {
467461
case constants.LegacyCommandTableCache:
468462
// we know there is just a single column - operation
469463
var isNull C.bool
470464
datum := C.slot_getattr(slot, 1, &isNull)
471465
operation := C.GoString(C.fdw_datumGetString(datum))
472-
hub, err := hub.GetHub()
473-
if err != nil {
474-
FdwError(err)
475-
return nil
476-
}
477-
if err := hub.HandleLegacyCacheCommand(operation); err != nil {
466+
if err := pluginHub.HandleLegacyCacheCommand(operation); err != nil {
478467
FdwError(err)
479468
return nil
480469
}
481470

482471
case constants.ForeignTableSettings:
483472
tupleDesc := buildTupleDesc(rel.rd_att)
484473
attributes := tupleDesc.Attrs
485-
hub, err := hub.GetHub()
486-
if err != nil {
487-
FdwError(err)
488-
return nil
489-
}
490474
var key *string
491475
var value *string
492476

@@ -520,7 +504,7 @@ func handleCommandInsert(rinfo *C.ResultRelInfo, slot *C.TupleTableSlot, rel C.R
520504
}
521505

522506
// apply the setting
523-
if err = hub.ApplySetting(*key, *value); err != nil {
507+
if err := pluginHub.ApplySetting(*key, *value); err != nil {
524508
FdwError(err)
525509
}
526510
return nil
@@ -548,10 +532,7 @@ func goFdwShutdown() {
548532
}
549533
}()
550534
log.Printf("[INFO] .\n******************************************************\n\n\t\tsteampipe postgres fdw shutdown\n\n******************************************************\n")
551-
pluginHub, err := hub.GetHub()
552-
if err != nil {
553-
FdwError(err)
554-
}
535+
pluginHub := hub.GetHub()
555536
pluginHub.Close()
556537
}
557538

0 commit comments

Comments
 (0)