pathsqlx

package module
v0.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 16, 2026 License: MIT Imports: 14 Imported by: 1

README

pathsqlx

The path engine implementations in Go for PathQL (see: PathQL.org).

Important Notes
  • Only tables can have a path - column paths are not supported
  • Aliases are preserved in the resulting JSON - any alias specified for tables or columns will be used in the output
  • Path hints can specify arrays - if the path ends with [], it's an array; otherwise, it's an object (single result), $ is the root object.
Algorithm

The path determination follows these steps:

  1. Query Analysis: The SQL query is parsed using the Vitess SQL parser. It identifies tables, their aliases, and how they are joined. It also extracts path hints from SQL comments (e.g., -- PATH alias $.path).
  2. Cardinality Detection: For each table, the algorithm determines if it represents a "one" or "many" relationship:
    • Explicit Hints: If a -- PATH hint ends with [], it's an array. If it's just $, it's a single object.
    • Foreign Keys: If table B has a foreign key to table A, a join from A to B is treated as one-to-many (array).
    • Join Type: In the absence of foreign key info, LEFT JOIN defaults to one-to-many.
    • Query Defaults: Queries with JOINs or no hints generally default to array results at the root.
  3. Path Generation: Based on cardinality and join structure:
    • Columns are mapped to paths like $.table.column (object) or $.table[].column (array).
    • Nesting is inferred by following the join tree from the root table.
Result Transformation

Once paths are determined, the flat database rows are transformed into a nested JSON structure:

  1. Record Collection: All rows are fetched from the database, and column values are associated with their inferred JSON paths.
  2. Grouping: Records are split into segments based on array markers ([]) in their paths.
  3. Entity Hashing: To handle duplicate data caused by SQL joins (e.g., a post appearing multiple times because it has multiple comments), pathsqlx generates MD5 hashes of the data at each nesting level. This unique fingerprint identifies specific entities even when they appear across multiple flattened rows.
  4. Tree Merging: Individual segments are merged into a single nested tree structure. The hashes ensure that child entities (like comments) are correctly attached to their specific parents (like posts) without duplicating the parent data.
  5. Finalization: The temporary hashes are removed, and the tree is converted into standard Go maps and slices, ready for JSON serialization.
Complete Example

Consider the following query that fetches a post and its comments:

SELECT 
    posts.id, posts.title, posts.content,
    comments.id, comments.message
FROM 
    posts, comments 
WHERE 
    comments.post_id = posts.id AND posts.id = 1 
-- PATH posts $.posts
1. Path Determination

The algorithm evaluates the query structure and database metadata:

  • Query Analysis: Identifies that posts and comments are related via the WHERE clause condition.
  • Cardinality Detection: Uses foreign key metadata to determine that one post can have multiple comments (one-to-many).
  • Hint Application: The hint -- PATH posts $.posts directs the engine to nest the results under a root posts key.
  • Inferred Paths:
    • posts => $.posts[] (Based on the hint, the [] is added because the table is an array)
    • comments => $.posts[].comments[] (Automatically nested inside the post object based on the detected relationship)

This results in the following column mapping:

SQL Column JSON Path
posts.id $.posts[].id
posts.title $.posts[].title
posts.content $.posts[].content
comments.id $.posts[].comments[].id
comments.message $.posts[].comments[].message
2. Result Transformation

The database returns flattened rows:

posts.id posts.title posts.content comments.id comments.message
1 Hello world! Welcome to the first post. 1 Hi!
1 Hello world! Welcome to the first post. 2 Thank you.

The engine processes these rows:

  1. Grouping: Detects the posts[] and comments[] markers.
  2. Entity Hashing: Generates an MD5 fingerprint for the post data. Both rows share this hash because the post ID and title are identical.
  3. Merging: The rows are merged. Because the post hashes match, they are combined into a single object, and the two unique comments are added to its comments array.
  4. Final Result: The JSON below is the result of the query.
{
    "posts": [
        {
            "id": 1,
            "title": "Hello world!",
            "content": "Welcome to the first post.",
            "comments": [
                {
                    "id": 1,
                    "message": "Hi!"
                },
                {
                    "id": 2,
                    "message": "Thank you."
                }
            ]
        }
    ]
}

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func SetMetadataFilename added in v0.1.1

func SetMetadataFilename(filename string)

SetMetadataFilename configures the metadata source. If filename is empty (default), metadata will be read from the database. If filename is non-empty, metadata will be loaded from that YAML file.

Types

type ByRevLen

type ByRevLen []string

ByRevLen is for reverse length-based sort.

func (ByRevLen) Len

func (a ByRevLen) Len() int

func (ByRevLen) Less

func (a ByRevLen) Less(i, j int) bool

func (ByRevLen) Swap

func (a ByRevLen) Swap(i, j int)

type DB

type DB struct {
	*sqlx.DB
	// contains filtered or unexported fields
}

DB is a wrapper around sqlx.DB

func Connect

func Connect(driverName, dataSourceName string) (*DB, error)

Connect opens a database connection and verifies with a ping.

func ConnectContext

func ConnectContext(ctx context.Context, driverName, dataSourceName string) (*DB, error)

ConnectContext opens a database connection and verifies with a ping, using the provided context.

func MustConnect

func MustConnect(driverName, dataSourceName string) *DB

MustConnect is the same as Connect, but panics on error.

func MustOpen

func MustOpen(driverName, dataSourceName string) *DB

MustOpen is the same as Open, but panics on error.

func NewDb

func NewDb(db *sql.DB, driverName string) *DB

NewDb returns a new pathsqlx.DB wrapper for an existing sql.DB.

func Open

func Open(driverName, dataSourceName string) (*DB, error)

Open opens a database connection. This is analogous to sql.Open, but returns a *pathsqlx.DB instead.

func (*DB) PathQuery

func (db *DB) PathQuery(query string, arg interface{}, hints map[string]string) (interface{}, error)

PathQuery is the query that returns nested paths hints parameter allows specifying path overrides for table aliases (e.g., {"posts": "$.posts", "$": "$.statistics"})

type JoinColumn

type JoinColumn struct {
	LeftAlias   string
	LeftColumn  string
	RightAlias  string
	RightColumn string
}

JoinColumn represents column information in a join condition

type JoinInfo

type JoinInfo struct {
	LeftAlias  string
	LeftTable  string
	RightAlias string
	RightTable string
	JoinType   string // "LEFT", "INNER", "RIGHT", etc.
	Condition  string
	OnColumns  []JoinColumn
}

JoinInfo represents information about a JOIN clause

type NamedStmt

type NamedStmt = sqlx.NamedStmt

Type aliases for sqlx types to enable drop-in replacement

type PathHint

type PathHint struct {
	Alias string
	Path  string
}

PathHint represents a path hint from SQL comments

type PathInferenceEngine

type PathInferenceEngine struct {
	// contains filtered or unexported fields
}

PathInferenceEngine infers JSON paths from query structure

func NewPathInferenceEngine

func NewPathInferenceEngine(metadata metadata.MetadataReader) *PathInferenceEngine

NewPathInferenceEngine creates a new path inference engine

func (*PathInferenceEngine) InferPaths

func (e *PathInferenceEngine) InferPaths(analysis *QueryAnalysis, columns []string) (map[string]string, error)

InferPaths generates JSON paths for query columns based on metadata and query structure

func (*PathInferenceEngine) InferPathsWithFallback

func (e *PathInferenceEngine) InferPathsWithFallback(analysis *QueryAnalysis, columns []string) map[string]string

InferPathsWithFallback is a helper that provides fallback behavior

func (*PathInferenceEngine) ValidatePaths

func (e *PathInferenceEngine) ValidatePaths(paths map[string]string) error

ValidatePaths checks if inferred paths are valid

type QueryAnalysis

type QueryAnalysis struct {
	Tables    map[string]string // alias -> table name
	Joins     []JoinInfo
	PathHints map[string]string // alias -> path override
}

QueryAnalysis contains the parsed query structure

func AnalyzeQuery

func AnalyzeQuery(sql string, hints map[string]string) (*QueryAnalysis, error)

AnalyzeQuery parses a SQL query to extract structure information Uses Vitess SQL parser to correctly handle subqueries, CTEs, and complex expressions Falls back to regex parsing if SQL parsing fails (e.g., for non-standard SQL) hints parameter provides path overrides for table aliases

func (*QueryAnalysis) GetJoinForTable

func (a *QueryAnalysis) GetJoinForTable(alias string) *JoinInfo

GetJoinForTable returns join information for a table alias

func (*QueryAnalysis) GetTableForAlias

func (a *QueryAnalysis) GetTableForAlias(alias string) (string, bool)

GetTableForAlias returns the table name for a given alias

type Result

type Result = sql.Result

Type aliases for sqlx types to enable drop-in replacement

type Row

type Row = sqlx.Row

Type aliases for sqlx types to enable drop-in replacement

type Rows

type Rows = sqlx.Rows

Type aliases for sqlx types to enable drop-in replacement

type Stmt

type Stmt = sqlx.Stmt

Type aliases for sqlx types to enable drop-in replacement

type Tx

type Tx = sqlx.Tx

Type aliases for sqlx types to enable drop-in replacement

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL