141 lines
4.2 KiB
Go
141 lines
4.2 KiB
Go
// Copyright 2025 Dolthub, Inc.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package plpgsql
|
|
|
|
import (
|
|
"strings"
|
|
|
|
"github.com/cockroachdb/errors"
|
|
)
|
|
|
|
// jsonConvert handles the conversion from the JSON format into a format that is easier to work with.
|
|
func jsonConvert(jsonBlock plpgSQL_block) (Block, error) {
|
|
block := Block{
|
|
TriggerNew: jsonBlock.NewVariableNumber,
|
|
TriggerOld: jsonBlock.OldVariableNumber,
|
|
Label: jsonBlock.Action.StmtBlock.Label,
|
|
}
|
|
lowestRecordNumber := int32(2147483647)
|
|
// We do a first loop to determine the offset for the first record
|
|
for _, v := range jsonBlock.Datums {
|
|
switch {
|
|
case v.Record != nil:
|
|
if v.Record.DatumNumber < lowestRecordNumber {
|
|
lowestRecordNumber = v.Record.DatumNumber
|
|
}
|
|
}
|
|
}
|
|
offset := int32(0) - lowestRecordNumber
|
|
// Then we do a second loop that actually adds all of the datums to the block
|
|
for _, v := range jsonBlock.Datums {
|
|
switch {
|
|
case v.Record != nil:
|
|
// TODO: support normal record types
|
|
datumNumber := v.Record.DatumNumber + offset
|
|
if int(datumNumber) >= len(block.Records) {
|
|
oldRecords := block.Records
|
|
block.Records = make([]Record, datumNumber+1)
|
|
copy(block.Records, oldRecords)
|
|
}
|
|
|
|
if v.Record.DatumNumber > 0 {
|
|
block.Records[datumNumber].Name = v.Record.RefName
|
|
}
|
|
case v.RecordField != nil:
|
|
recordParentNumber := v.RecordField.RecordParentNumber + offset
|
|
if int(recordParentNumber) >= len(block.Records) {
|
|
return Block{}, errors.New("invalid record parent number")
|
|
}
|
|
block.Records[recordParentNumber].Fields = append(
|
|
block.Records[recordParentNumber].Fields, v.RecordField.FieldName)
|
|
case v.Row != nil:
|
|
case v.Variable != nil:
|
|
block.Variables = append(block.Variables, Variable{
|
|
Name: v.Variable.RefName,
|
|
Type: strings.ToLower(v.Variable.Type.Type.Name),
|
|
IsParameter: v.Variable.LineNumber == 0,
|
|
Default: v.Variable.Default.Var.Query,
|
|
})
|
|
default:
|
|
return Block{}, errors.Errorf("unhandled datum type: %T", v)
|
|
}
|
|
}
|
|
var err error
|
|
block.Body, err = jsonConvertStatements(jsonBlock.Action.StmtBlock.Body)
|
|
if err != nil {
|
|
return Block{}, err
|
|
}
|
|
return block, nil
|
|
}
|
|
|
|
// jsonConvertStatement converts a statement in JSON form to the output form.
|
|
func jsonConvertStatement(stmt statement) (Statement, error) {
|
|
switch {
|
|
case stmt.Assignment != nil:
|
|
return stmt.Assignment.Convert()
|
|
case stmt.Block != nil:
|
|
stmts, err := jsonConvertStatements(stmt.Block.Body)
|
|
if err != nil {
|
|
return Block{}, err
|
|
}
|
|
return Block{
|
|
Body: stmts,
|
|
}, nil
|
|
case stmt.Call != nil:
|
|
return stmt.Call.Convert()
|
|
case stmt.Case != nil:
|
|
return stmt.Case.Convert()
|
|
case stmt.DynExec != nil:
|
|
return stmt.DynExec.Convert()
|
|
case stmt.ExecSQL != nil:
|
|
return stmt.ExecSQL.Convert()
|
|
case stmt.Exit != nil:
|
|
return stmt.Exit.Convert(), nil
|
|
case stmt.ForILoop != nil:
|
|
return stmt.ForILoop.Convert()
|
|
case stmt.ForSLoop != nil:
|
|
return stmt.ForSLoop.Convert()
|
|
case stmt.If != nil:
|
|
return stmt.If.Convert()
|
|
case stmt.Loop != nil:
|
|
return stmt.Loop.Convert()
|
|
case stmt.Perform != nil:
|
|
return stmt.Perform.Convert(), nil
|
|
case stmt.Raise != nil:
|
|
return stmt.Raise.Convert(), nil
|
|
case stmt.Return != nil:
|
|
return stmt.Return.Convert(), nil
|
|
case stmt.ReturnQuery != nil:
|
|
return stmt.ReturnQuery.Convert(), nil
|
|
case stmt.While != nil:
|
|
return stmt.While.Convert()
|
|
default:
|
|
return Block{}, errors.Errorf("unhandled statement type: %T", stmt)
|
|
}
|
|
}
|
|
|
|
// jsonConvertStatements converts a collection of statements in JSON form to their output form.
|
|
func jsonConvertStatements(stmts []statement) ([]Statement, error) {
|
|
newStmts := make([]Statement, len(stmts))
|
|
for i, stmt := range stmts {
|
|
var err error
|
|
newStmts[i], err = jsonConvertStatement(stmt)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
return newStmts, nil
|
|
}
|