Skip to content

Commit 90a694e

Browse files
committed
feat (v2): v2
1 parent 9b61a30 commit 90a694e

Some content is hidden

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

107 files changed

+6557
-7524
lines changed

cypher/models/cypher/model.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -770,14 +770,23 @@ type Literal struct {
770770
}
771771

772772
func NewLiteral(value any, null bool) *Literal {
773+
if !null {
774+
if strValue, typeOK := value.(string); typeOK {
775+
return NewStringLiteral(strValue)
776+
}
777+
}
778+
773779
return &Literal{
774780
Value: value,
775781
Null: null,
776782
}
777783
}
778784

779785
func NewStringLiteral(value string) *Literal {
780-
return NewLiteral("'"+value+"'", false)
786+
return &Literal{
787+
Value: "'" + value + "'",
788+
Null: false,
789+
}
781790
}
782791

783792
func (s *Literal) copy() *Literal {

cypher/models/pgsql/format/format.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"fmt"
55
"strconv"
66
"strings"
7+
"time"
78

89
"github.com/specterops/dawgs/cypher/models/pgsql"
910
)
@@ -70,6 +71,9 @@ func formatSlice[T any, TS []T](builder *OutputBuilder, slice TS, dataType pgsql
7071

7172
func formatValue(builder *OutputBuilder, value any) error {
7273
switch typedValue := value.(type) {
74+
case time.Time:
75+
builder.Write("'", typedValue.Format(time.RFC3339Nano), "'::timestamp with time zone")
76+
7377
case uint:
7478
builder.Write(strconv.FormatUint(uint64(typedValue), 10))
7579

@@ -116,7 +120,11 @@ func formatValue(builder *OutputBuilder, value any) error {
116120
return formatSlice(builder, typedValue, pgsql.Int8Array)
117121

118122
case string:
119-
builder.Write("'", typedValue, "'")
123+
// Double single quotes per SQL string literal rules
124+
builder.Write("'", strings.ReplaceAll(typedValue, "'", "''"), "'")
125+
126+
case []string:
127+
return formatSlice(builder, typedValue, pgsql.TextArray)
120128

121129
case bool:
122130
builder.Write(strconv.FormatBool(typedValue))

cypher/models/pgsql/test/testcase.go

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,13 @@ import (
1313
"testing"
1414
"time"
1515

16-
"github.com/specterops/dawgs/drivers/pg"
17-
1816
"cuelang.org/go/pkg/regexp"
1917
"github.com/specterops/dawgs/cypher/frontend"
2018
"github.com/specterops/dawgs/cypher/models/cypher"
2119
"github.com/specterops/dawgs/cypher/models/pgsql"
2220
"github.com/specterops/dawgs/cypher/models/pgsql/translate"
2321
"github.com/specterops/dawgs/cypher/models/walk"
22+
"github.com/specterops/dawgs/database"
2423
"github.com/stretchr/testify/require"
2524
)
2625

@@ -178,7 +177,7 @@ func (s *TranslationTestCase) Assert(t *testing.T, expectedSQL string, kindMappe
178177
}
179178
}
180179

181-
func (s *TranslationTestCase) AssertLive(ctx context.Context, t *testing.T, driver *pg.Driver) {
180+
func (s *TranslationTestCase) AssertLive(ctx context.Context, t *testing.T, db database.Instance) {
182181
if regularQuery, err := frontend.ParseCypher(frontend.NewContext(), s.Cypher); err != nil {
183182
t.Fatalf("Failed to compile cypher query: %s - %v", s.Cypher, err)
184183
} else {
@@ -195,13 +194,15 @@ func (s *TranslationTestCase) AssertLive(ctx context.Context, t *testing.T, driv
195194
}
196195
}
197196

198-
if translation, err := translate.Translate(context.Background(), regularQuery, driver.KindMapper(), s.CypherParams); err != nil {
199-
t.Fatalf("Failed to translate cypher query: %s - %v", s.Cypher, err)
200-
} else if formattedQuery, err := translate.Translated(translation); err != nil {
201-
t.Fatalf("Failed to format SQL translatedQuery: %v", err)
202-
} else {
203-
require.Nil(t, driver.Run(ctx, "explain "+formattedQuery, translation.Parameters))
204-
}
197+
require.NoError(t, db.Session(ctx, func(ctx context.Context, driver database.Driver) error {
198+
result := driver.Explain(ctx, regularQuery, s.CypherParams)
199+
200+
if err := result.Close(ctx); err != nil {
201+
return err
202+
}
203+
204+
return result.Error()
205+
}))
205206
}
206207
}
207208

cypher/models/pgsql/test/translation_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import (
77
"strings"
88
"testing"
99

10-
"github.com/specterops/dawgs/drivers/pg/pgutil"
10+
"github.com/specterops/dawgs/database/pg/pgutil"
1111

1212
"github.com/specterops/dawgs/cypher/models/pgsql"
1313
"github.com/specterops/dawgs/graph"

cypher/models/pgsql/test/validation_integration_test.go

Lines changed: 15 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,10 @@ import (
99
"runtime/debug"
1010
"testing"
1111

12-
"github.com/jackc/pgx/v5/pgxpool"
12+
"github.com/specterops/dawgs/database"
1313

1414
"github.com/specterops/dawgs"
15-
"github.com/specterops/dawgs/drivers/pg"
15+
"github.com/specterops/dawgs/database/pg"
1616
"github.com/specterops/dawgs/graph"
1717
"github.com/specterops/dawgs/util/size"
1818
"github.com/stretchr/testify/require"
@@ -32,34 +32,25 @@ func TestTranslationTestCases(t *testing.T) {
3232

3333
require.NotEmpty(t, pgConnectionStr)
3434

35-
if pgxPool, err := pgxpool.New(testCtx, pgConnectionStr); err != nil {
36-
t.Fatalf("Failed opening database connection: %v", err)
37-
} else if connection, err := dawgs.Open(context.TODO(), pg.DriverName, dawgs.Config{
35+
if connection, err := dawgs.Open(context.TODO(), pg.DriverName, dawgs.Config{
3836
GraphQueryMemoryLimit: size.Gibibyte,
39-
Pool: pgxPool,
37+
ConnectionString: pgConnectionStr,
4038
}); err != nil {
4139
t.Fatalf("Failed opening database connection: %v", err)
42-
} else if pgConnection, typeOK := connection.(*pg.Driver); !typeOK {
43-
t.Fatalf("Invalid connection type: %T", connection)
4440
} else {
4541
defer connection.Close(testCtx)
4642

47-
graphSchema := graph.Schema{
48-
Graphs: []graph.Graph{{
49-
Name: "test",
50-
Nodes: graph.Kinds{
51-
graph.StringKind("NodeKind1"),
52-
graph.StringKind("NodeKind2"),
53-
},
54-
Edges: graph.Kinds{
55-
graph.StringKind("EdgeKind1"),
56-
graph.StringKind("EdgeKind2"),
57-
},
58-
}},
59-
DefaultGraph: graph.Graph{
60-
Name: "test",
43+
graphSchema := database.NewSchema("test", database.Graph{
44+
Name: "test",
45+
Nodes: graph.Kinds{
46+
graph.StringKind("NodeKind1"),
47+
graph.StringKind("NodeKind2"),
6148
},
62-
}
49+
Edges: graph.Kinds{
50+
graph.StringKind("EdgeKind1"),
51+
graph.StringKind("EdgeKind2"),
52+
},
53+
})
6354

6455
if err := connection.AssertSchema(testCtx, graphSchema); err != nil {
6556
t.Fatalf("Failed asserting graph schema: %v", err)
@@ -79,7 +70,7 @@ func TestTranslationTestCases(t *testing.T) {
7970
}
8071
}()
8172

82-
testCase.AssertLive(testCtx, t, pgConnection)
73+
testCase.AssertLive(testCtx, t, connection)
8374
})
8475

8576
casesRun += 1

cypher/models/pgsql/visualization/visualizer_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import (
55
"context"
66
"testing"
77

8-
"github.com/specterops/dawgs/drivers/pg/pgutil"
8+
"github.com/specterops/dawgs/database/pg/pgutil"
99

1010
"github.com/specterops/dawgs/cypher/frontend"
1111
"github.com/specterops/dawgs/cypher/models/pgsql/translate"

database/driver.go

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
package database
2+
3+
import (
4+
"context"
5+
6+
"github.com/specterops/dawgs/graph"
7+
8+
"github.com/specterops/dawgs/cypher/models/cypher"
9+
)
10+
11+
type Option int
12+
13+
const (
14+
OptionReadOnly Option = 0
15+
OptionReadWrite Option = 1
16+
)
17+
18+
type Result interface {
19+
HasNext(ctx context.Context) bool
20+
Scan(scanTargets ...any) error
21+
Error() error
22+
Close(ctx context.Context) error
23+
24+
// Values returns the next values array from the result.
25+
//
26+
// Deprecated: This function will be removed in future version.
27+
Values() []any
28+
}
29+
30+
type Driver interface {
31+
WithGraph(target Graph) Driver
32+
33+
CreateNode(ctx context.Context, node *graph.Node) (graph.ID, error)
34+
CreateRelationship(ctx context.Context, relationship *graph.Relationship) (graph.ID, error)
35+
36+
Exec(ctx context.Context, query *cypher.RegularQuery, parameters map[string]any) Result
37+
Explain(ctx context.Context, query *cypher.RegularQuery, parameters map[string]any) Result
38+
Profile(ctx context.Context, query *cypher.RegularQuery, parameters map[string]any) Result
39+
Mapper() graph.ValueMapper
40+
}
41+
42+
type QueryLogic func(ctx context.Context, driver Driver) error
43+
44+
type Instance interface {
45+
AssertSchema(ctx context.Context, schema Schema) error
46+
Session(ctx context.Context, driverLogic QueryLogic, options ...Option) error
47+
Transaction(ctx context.Context, driverLogic QueryLogic, options ...Option) error
48+
Close(ctx context.Context) error
49+
50+
// FetchKinds retrieves the complete list of kinds available to the database.
51+
FetchKinds(ctx context.Context) (graph.Kinds, error)
52+
}
53+
54+
type errorResult struct {
55+
err error
56+
}
57+
58+
func (s errorResult) HasNext(ctx context.Context) bool {
59+
return false
60+
}
61+
62+
func (s errorResult) Scan(scanTargets ...any) error {
63+
return s.err
64+
}
65+
66+
func (s errorResult) Error() error {
67+
return s.err
68+
}
69+
70+
func (s errorResult) Values() []any {
71+
return nil
72+
}
73+
74+
func (s errorResult) Close(ctx context.Context) error {
75+
return nil
76+
}
77+
78+
func NewErrorResult(err error) Result {
79+
return errorResult{
80+
err: err,
81+
}
82+
}

0 commit comments

Comments
 (0)