Skip to content

Commit 18933ee

Browse files
insert-returning (#552)
Summary: - Support for `INSERT RETURNING`. - Documented expected behaviour. - Working for synchronously created objects **only**. - Added robot test `Insert Returning Simple Projection`.
1 parent f06ad23 commit 18933ee

File tree

21 files changed

+305
-56
lines changed

21 files changed

+305
-56
lines changed

.vscode/launch.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,7 @@
175175
"select BackupId, BackupState from aws.cloudhsm.backups where region = 'rubbish-region' order by BackupId;",
176176
"select rings.projectsId as project, rings.locationsId as locale, split_part(rings.name, '/', -1) as key_ring_name, split_part(keys.name, '/', -1) as key_name, json_extract(keys.\"versionTemplate\", '$.algorithm') as key_algorithm, json_extract(keys.\"versionTemplate\", '$.protectionLevel') as key_protection_level from google.cloudkms.key_rings rings inner join google.cloudkms.crypto_keys keys on keys.keyRingsId = split_part(rings.name, '/', -1) and keys.projectsId = rings.projectsId and keys.locationsId = rings.locationsId where rings.projectsId in ('testing-project', 'testing-project-two', 'testing-project-three') and rings.locationsId in ('global', 'australia-southeast1', 'australia-southeast2') order by project, locale, key_name ;",
177177
"delete from aws.cloud_control.resources where region = 'ap-southeast-1' and data__TypeName = 'AWS::Logs::LogGroup' and data__Identifier = 'LogGroupResourceExampleThird' ;",
178+
"insert into google.storage.buckets( project, data__name) select 'testing-project', 'silly-bucket' returning projectNumber;",
178179
],
179180
"default": "show providers;"
180181
},

docs/developer_guide.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,3 +268,13 @@ time ./stackql exec --cpuprofile=./select-disks-improved-05.profile --auth='{ "g
268268
## AWS HTTP request signing
269269

270270
https://docs.aws.amazon.com/sdk-for-go/api/aws/signer/v4/
271+
272+
## Selection from non-select DML
273+
274+
`INSERT RETURNING` can function in two mechanisms:
275+
276+
- Synchronous responses, such as [`google.storage.buckets`](https://cloud.google.com/storage/docs/json_api/v1/buckets/insert). The returning clause is a projection on the immediately available reponse body.
277+
- Asynchronous responses, such as [`google.compute.instances`](https://cloud.google.com/compute/docs/reference/rest/v1/instances/insert). The returning clause is a projection on the reponse body **after** the await flow has concluded.
278+
279+
Future use cases for `UPDATE RETURNING`, `REPLACE RETURNING` and `DELETE RETURNING` will function the same observable fashion.
280+

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ require (
2222
github.com/stackql/any-sdk v0.1.3-beta02
2323
github.com/stackql/go-suffix-map v0.0.1-alpha01
2424
github.com/stackql/psql-wire v0.1.1-beta23
25-
github.com/stackql/stackql-parser v0.0.14-alpha05
25+
github.com/stackql/stackql-parser v0.0.15-alpha06
2626
github.com/stretchr/testify v1.10.0
2727
golang.org/x/sync v0.10.0
2828
gonum.org/v1/gonum v0.11.0

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -494,8 +494,8 @@ github.com/stackql/readline v0.0.2-alpha05 h1:ID4QzGdplFBsrSnTuz8pvKzWw96JbrJg8f
494494
github.com/stackql/readline v0.0.2-alpha05/go.mod h1:OFAYOdXk/X4+5GYiDXFfaGrk+bCN6Qv0SYY5HNzD2E0=
495495
github.com/stackql/stackql-go-sqlite3 v1.0.3-stackql h1:j0yt6T5thZuz5+HIr81PXz2AClAtCko0vzr5tm8C1g8=
496496
github.com/stackql/stackql-go-sqlite3 v1.0.3-stackql/go.mod h1:HemqCrcMK2xyhMMMt6oZ7ERDtoSmyyDsw5LBBcTZ+Rk=
497-
github.com/stackql/stackql-parser v0.0.14-alpha05 h1:DLcsaeTypH5p1T+g/9NqaGYdC9uIQ+7pZetnkjHi0G0=
498-
github.com/stackql/stackql-parser v0.0.14-alpha05/go.mod h1:iyB47SvRS+Fvpn7joF7mHAkeiWSq83TbUhglRmLzPLQ=
497+
github.com/stackql/stackql-parser v0.0.15-alpha06 h1:bdaudybbEmrR9m88CO1HpX/eKda2+gjiy4Kf6ZjwPoQ=
498+
github.com/stackql/stackql-parser v0.0.15-alpha06/go.mod h1:iyB47SvRS+Fvpn7joF7mHAkeiWSq83TbUhglRmLzPLQ=
499499
github.com/stackql/stackql-provider-registry v0.0.1-rc06 h1:MgroWOr0bSqjSTDGnXB0UoZGFXpW3SRtN0EFkzB8Rpo=
500500
github.com/stackql/stackql-provider-registry v0.0.1-rc06/go.mod h1:87rVxnS2aRASK20lBQgoYA0o7FSJTZBGGRaWFR7IDm4=
501501
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=

internal/stackql/internal_data_transfer/builder_input/builder_input.go

Lines changed: 30 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"github.com/stackql/stackql/internal/stackql/internal_data_transfer/internaldto"
1010
"github.com/stackql/stackql/internal/stackql/primitivegraph"
1111
"github.com/stackql/stackql/internal/stackql/provider"
12+
"github.com/stackql/stackql/internal/stackql/tableinsertioncontainer"
1213
"github.com/stackql/stackql/internal/stackql/tablemetadata"
1314
)
1415

@@ -51,27 +52,30 @@ type BuilderInput interface {
5152
SetIsTargetPhysicalTable(isPhysical bool)
5253
SetTxnCtrlCtrs(internaldto.TxnControlCounters)
5354
GetTxnCtrlCtrs() (internaldto.TxnControlCounters, bool)
55+
GetTableInsertionContainer() (tableinsertioncontainer.TableInsertionContainer, bool)
56+
SetTableInsertionContainer(tableinsertioncontainer.TableInsertionContainer)
5457
}
5558

5659
type builderInput struct {
57-
graphHolder primitivegraph.PrimitiveGraphHolder
58-
handlerCtx handler.HandlerContext
59-
paramMap map[int]map[string]interface{}
60-
tbl tablemetadata.ExtendedTableMetadata
61-
dependencyNode primitivegraph.PrimitiveNode
62-
commentDirectives sqlparser.CommentDirectives
63-
isAwait bool
64-
verb string
65-
inputAlias string
66-
isUndo bool
67-
node sqlparser.SQLNode
68-
paramMapStream streaming.MapStream
69-
httpPrepStream anysdk.HttpPreparatorStream
70-
op anysdk.OperationStore
71-
prov provider.IProvider
72-
annotatedAst annotatedast.AnnotatedAst
73-
isTargetPhysical bool
74-
txnCtrlCtrs internaldto.TxnControlCounters
60+
graphHolder primitivegraph.PrimitiveGraphHolder
61+
handlerCtx handler.HandlerContext
62+
paramMap map[int]map[string]interface{}
63+
tbl tablemetadata.ExtendedTableMetadata
64+
dependencyNode primitivegraph.PrimitiveNode
65+
commentDirectives sqlparser.CommentDirectives
66+
isAwait bool
67+
verb string
68+
inputAlias string
69+
isUndo bool
70+
node sqlparser.SQLNode
71+
paramMapStream streaming.MapStream
72+
httpPrepStream anysdk.HttpPreparatorStream
73+
op anysdk.OperationStore
74+
prov provider.IProvider
75+
annotatedAst annotatedast.AnnotatedAst
76+
isTargetPhysical bool
77+
txnCtrlCtrs internaldto.TxnControlCounters
78+
tableInsertionContainer tableinsertioncontainer.TableInsertionContainer
7579
}
7680

7781
func NewBuilderInput(
@@ -88,6 +92,14 @@ func NewBuilderInput(
8892
}
8993
}
9094

95+
func (bi *builderInput) GetTableInsertionContainer() (tableinsertioncontainer.TableInsertionContainer, bool) {
96+
return bi.tableInsertionContainer, bi.tableInsertionContainer != nil
97+
}
98+
99+
func (bi *builderInput) SetTableInsertionContainer(ti tableinsertioncontainer.TableInsertionContainer) {
100+
bi.tableInsertionContainer = ti
101+
}
102+
91103
func (bi *builderInput) SetTxnCtrlCtrs(tcc internaldto.TxnControlCounters) {
92104
bi.txnCtrlCtrs = tcc
93105
}

internal/stackql/parserutil/parser_util.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,26 @@ func ExtractSelectColumnNames(selStmt *sqlparser.Select, formatter sqlparser.Nod
8888
return colNames, err
8989
}
9090

91+
func ExtractInsertReturningColumnNames(
92+
insertStmt *sqlparser.Insert,
93+
formatter sqlparser.NodeFormatter,
94+
) ([]ColumnHandle, error) {
95+
var colNames []ColumnHandle
96+
var err error
97+
for _, node := range insertStmt.SelectExprs {
98+
switch node := node.(type) {
99+
case *sqlparser.AliasedExpr:
100+
cn, cErr := inferColNameFromExpr(node.Expr, formatter, node.As.GetRawVal())
101+
if cErr != nil {
102+
return nil, cErr
103+
}
104+
colNames = append(colNames, cn)
105+
case *sqlparser.StarExpr:
106+
}
107+
}
108+
return colNames, err
109+
}
110+
91111
func ExtractInsertColumnNames(insertStmt *sqlparser.Insert) ([]string, error) {
92112
var colNames []string
93113
var err error

internal/stackql/planbuilder/plan_builder.go

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
"github.com/stackql/stackql/internal/stackql/primitivebuilder"
2525
"github.com/stackql/stackql/internal/stackql/primitivegenerator"
2626
"github.com/stackql/stackql/internal/stackql/primitivegraph"
27+
"github.com/stackql/stackql/internal/stackql/tableinsertioncontainer"
2728
"github.com/stackql/stackql/internal/stackql/tablemetadata"
2829
"github.com/stackql/stackql/internal/stackql/util"
2930

@@ -919,9 +920,36 @@ func (pgb *standardPlanGraphBuilder) handleInsert(pbi planbuilderinput.PlanBuild
919920
if isPhysicalTable {
920921
bldrInput.SetIsTargetPhysicalTable(true)
921922
}
922-
bldr := primitivebuilder.NewInsertOrUpdate(
923-
bldrInput,
924-
)
923+
var bldr primitivebuilder.Builder
924+
if len(node.SelectExprs) > 0 {
925+
// Two cases:
926+
// 1. Synchronous. Equivalent to select.
927+
// 2. Asynchronous. Whole other story.
928+
// Synchronous only for now...
929+
tableMeta, tableMetaExists := bldrInput.GetTableMetadata()
930+
if !tableMetaExists {
931+
return fmt.Errorf("could not obtain table metadata for node '%s'", node.Action)
932+
}
933+
rc, rcErr := tableinsertioncontainer.NewTableInsertionContainer(
934+
tableMeta,
935+
handlerCtx.GetSQLEngine(),
936+
handlerCtx.GetTxnCounterMgr(),
937+
)
938+
if rcErr != nil {
939+
return rcErr
940+
}
941+
bldrInput.SetTableInsertionContainer(rc)
942+
bldr = primitivebuilder.NewSingleAcquireAndSelect(
943+
bldrInput,
944+
primitiveGenerator.GetPrimitiveComposer().GetInsertPreparedStatementCtx(),
945+
primitiveGenerator.GetPrimitiveComposer().GetSelectPreparedStatementCtx(),
946+
nil,
947+
)
948+
} else {
949+
bldr = primitivebuilder.NewInsertOrUpdate(
950+
bldrInput,
951+
)
952+
}
925953
err = bldr.Build()
926954
if err != nil {
927955
return err

internal/stackql/primitivebuilder/exec.go

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"github.com/stackql/stackql/internal/stackql/drm"
77
"github.com/stackql/stackql/internal/stackql/execution"
88
"github.com/stackql/stackql/internal/stackql/handler"
9+
"github.com/stackql/stackql/internal/stackql/internal_data_transfer/builder_input"
910
"github.com/stackql/stackql/internal/stackql/internal_data_transfer/internaldto"
1011
"github.com/stackql/stackql/internal/stackql/internal_data_transfer/primitive_context"
1112
"github.com/stackql/stackql/internal/stackql/primitive"
@@ -92,11 +93,14 @@ func (ss *Exec) Build() error {
9293
return analysisErr
9394
}
9495
methodAnalysisOutput.GetInsertTabulation()
95-
deFactoSelectBuilder := NewSingleAcquireAndSelect(
96+
bldrInput := builder_input.NewBuilderInput(
9697
ss.graph,
97-
ss.tcc,
98-
ss.handlerCtx.Clone(),
99-
nil,
98+
handlerCtx.Clone(),
99+
tbl,
100+
)
101+
bldrInput.SetTxnCtrlCtrs(ss.tcc)
102+
deFactoSelectBuilder := NewSingleAcquireAndSelect(
103+
bldrInput,
100104
nil,
101105
nil,
102106
nil,

internal/stackql/primitivebuilder/insert_or_update.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,28 +8,28 @@ import (
88
"github.com/stackql/stackql/internal/stackql/primitivegraph"
99
)
1010

11-
type InsertOrUpdate struct {
11+
type insertOrUpdate struct {
1212
bldrInput builder_input.BuilderInput
1313
root primitivegraph.PrimitiveNode
1414
}
1515

1616
func NewInsertOrUpdate(
1717
bldrInput builder_input.BuilderInput,
1818
) Builder {
19-
return &InsertOrUpdate{
19+
return &insertOrUpdate{
2020
bldrInput: bldrInput,
2121
}
2222
}
2323

24-
func (ss *InsertOrUpdate) GetRoot() primitivegraph.PrimitiveNode {
24+
func (ss *insertOrUpdate) GetRoot() primitivegraph.PrimitiveNode {
2525
return ss.root
2626
}
2727

28-
func (ss *InsertOrUpdate) GetTail() primitivegraph.PrimitiveNode {
28+
func (ss *insertOrUpdate) GetTail() primitivegraph.PrimitiveNode {
2929
return ss.root
3030
}
3131

32-
func (ss *InsertOrUpdate) Build() error {
32+
func (ss *insertOrUpdate) Build() error {
3333
node, nodeExists := ss.bldrInput.GetParserNode()
3434
if !nodeExists {
3535
return fmt.Errorf("mutation executor: node does not exist")

internal/stackql/primitivebuilder/single_acquire_and_select.go

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,7 @@ package primitivebuilder
33
import (
44
"github.com/stackql/any-sdk/pkg/streaming"
55
"github.com/stackql/stackql/internal/stackql/drm"
6-
"github.com/stackql/stackql/internal/stackql/handler"
7-
"github.com/stackql/stackql/internal/stackql/internal_data_transfer/internaldto"
6+
"github.com/stackql/stackql/internal/stackql/internal_data_transfer/builder_input"
87
"github.com/stackql/stackql/internal/stackql/primitivegraph"
98
"github.com/stackql/stackql/internal/stackql/tableinsertioncontainer"
109
)
@@ -13,17 +12,24 @@ type SingleAcquireAndSelect struct {
1312
graph primitivegraph.PrimitiveGraphHolder
1413
acquireBuilder Builder
1514
selectBuilder Builder
15+
bldrInput builder_input.BuilderInput
16+
root primitivegraph.PrimitiveNode
1617
}
1718

1819
func NewSingleAcquireAndSelect(
19-
graph primitivegraph.PrimitiveGraphHolder,
20-
txnControlCounters internaldto.TxnControlCounters, //nolint:revive // future proofing
21-
handlerCtx handler.HandlerContext,
22-
insertContainer tableinsertioncontainer.TableInsertionContainer,
20+
// graph primitivegraph.PrimitiveGraphHolder,
21+
// txnControlCounters internaldto.TxnControlCounters, //nolint:revive // future proofing
22+
// handlerCtx handler.HandlerContext,
23+
// insertContainer tableinsertioncontainer.TableInsertionContainer,
24+
bldrInput builder_input.BuilderInput,
2325
insertCtx drm.PreparedStatementCtx,
2426
selectCtx drm.PreparedStatementCtx,
2527
rowSort func(map[string]map[string]interface{}) []string,
2628
) Builder {
29+
graph, _ := bldrInput.GetGraphHolder()
30+
// txnControlCounters, _ := bldrInput.GetTxnCtrlCtrs()
31+
handlerCtx, _ := bldrInput.GetHandlerContext()
32+
insertContainer, _ := bldrInput.GetTableInsertionContainer()
2733
return &SingleAcquireAndSelect{
2834
graph: graph,
2935
acquireBuilder: NewSingleSelectAcquire(
@@ -38,6 +44,7 @@ func NewSingleAcquireAndSelect(
3844
[]tableinsertioncontainer.TableInsertionContainer{insertContainer},
3945
rowSort,
4046
streaming.NewNopMapStream()),
47+
bldrInput: bldrInput,
4148
}
4249
}
4350

@@ -60,5 +67,14 @@ func (ss *SingleAcquireAndSelect) Build() error {
6067
}
6168
graph := ss.graph
6269
graph.NewDependency(ss.acquireBuilder.GetTail(), ss.selectBuilder.GetRoot(), 1.0)
70+
rootNode := ss.acquireBuilder.GetRoot()
71+
ss.root = rootNode
72+
dependencyNode, dependencyNodeExists := ss.bldrInput.GetDependencyNode()
73+
if dependencyNodeExists {
74+
//nolint:errcheck // TODO: fix this
75+
rootNode.SetInputAlias("", dependencyNode.ID())
76+
ss.graph.NewDependency(dependencyNode, rootNode, 1.0)
77+
// ss.root = dependencyNode // dont think this is needed
78+
}
6379
return nil
6480
}

0 commit comments

Comments
 (0)