diff --git a/flake.lock b/flake.lock index c2e2c3d..522a084 100644 --- a/flake.lock +++ b/flake.lock @@ -2,18 +2,15 @@ "nodes": { "nixpkgs": { "locked": { - "lastModified": 1764983851, - "narHash": "sha256-y7RPKl/jJ/KAP/VKLMghMgXTlvNIJMHKskl8/Uuar7o=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "d9bc5c7dceb30d8d6fafa10aeb6aa8a48c218454", - "type": "github" + "lastModified": 1767379071, + "narHash": "sha256-3xDI4xtzovwqE/eAxCwmXxUqBg6Yoam2L1u0IwRNhW4=", + "rev": "fb7944c166a3b630f177938e478f0378e64ce108", + "type": "tarball", + "url": "https://releases.nixos.org/nixos/unstable/nixos-26.05pre921484.fb7944c166a3/nixexprs.tar.xz?lastModified=1767379071&rev=fb7944c166a3b630f177938e478f0378e64ce108" }, "original": { - "owner": "NixOS", - "ref": "nixos-25.11", - "repo": "nixpkgs", - "type": "github" + "type": "tarball", + "url": "https://channels.nixos.org/nixos-unstable/nixexprs.tar.xz" } }, "root": { diff --git a/flake.nix b/flake.nix index 701b67a..533772b 100644 --- a/flake.nix +++ b/flake.nix @@ -1,28 +1,37 @@ { description = "sqlite-store"; inputs = { - nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.11"; + nixpkgs.url = "https://channels.nixos.org/nixos-unstable/nixexprs.tar.xz"; systems.url = "github:nix-systems/default"; - }; - outputs = { self, nixpkgs, systems, ... }@inputs: + outputs = + { + nixpkgs, + systems, + ... + }: let - eachSystem = f: - nixpkgs.lib.genAttrs (import systems) - (system: f system nixpkgs.legacyPackages.${system}); - in { + eachSystem = + f: nixpkgs.lib.genAttrs (import systems) (system: f system nixpkgs.legacyPackages.${system}); + in + { - devShells = eachSystem (system: pkgs: { - default = pkgs.mkShell { - shellHook = '' - # Set here the env vars you want to be available in the shell - ''; - hardeningDisable = [ "all" ]; + devShells = eachSystem ( + system: pkgs: { + default = pkgs.mkShell { + shellHook = '' + # Set here the env vars you want to be available in the shell + ''; + hardeningDisable = [ "all" ]; - packages = with pkgs; [ go sqlite ]; - }; - }); + packages = with pkgs; [ + go + sqlite + ]; + }; + } + ); }; } diff --git a/query/exists_method.go b/query/exists_method.go index ccde3e5..a2f0e34 100644 --- a/query/exists_method.go +++ b/query/exists_method.go @@ -43,7 +43,7 @@ func (e ExistsEvaluator) EvaluateAST(ast *AST, options *QueryOptions) (*SelectQu sortingTable := fmt.Sprintf("arkiv_annotation_sorting%d", i) - keyPlaceholder := builder.pushArgument(orderBy.Name) + keyPlaceholder := builder.PushArgument(orderBy.Name) fmt.Fprintf(builder.queryBuilder, " LEFT JOIN %[1]s AS %s INDEXED BY %[4]s"+ @@ -58,7 +58,7 @@ func (e ExistsEvaluator) EvaluateAST(ast *AST, options *QueryOptions) (*SelectQu ) } - err := builder.addPaginationArguments() + err := AddPaginationArguments(&builder) if err != nil { return nil, fmt.Errorf("error adding the pagination condition: %w", err) } @@ -70,7 +70,7 @@ func (e ExistsEvaluator) EvaluateAST(ast *AST, options *QueryOptions) (*SelectQu builder.queryBuilder.WriteString(" AND ") } - blockArg := builder.pushArgument(builder.options.AtBlock) + blockArg := builder.PushArgument(builder.options.AtBlock) fmt.Fprintf(builder.queryBuilder, "%s BETWEEN e.from_block AND e.to_block - 1", blockArg) if ast.Expr != nil { @@ -147,14 +147,14 @@ func (ExistsEvaluator) addTermConditions(term *ASTTerm, b *QueryBuilder) error { ) if term.Assign != nil { - key = b.pushArgument(term.Assign.Var) + key = b.PushArgument(term.Assign.Var) val := term.Assign.Value if val.String != nil { attrType = "string" - value = b.pushArgument(*val.String) + value = b.PushArgument(*val.String) } else { attrType = "numeric" - value = b.pushArgument(*val.Number) + value = b.PushArgument(*val.Number) } operation = "=" @@ -162,7 +162,7 @@ func (ExistsEvaluator) addTermConditions(term *ASTTerm, b *QueryBuilder) error { operation = "!=" } } else if term.Inclusion != nil { - key = b.pushArgument(term.Inclusion.Var) + key = b.PushArgument(term.Inclusion.Var) var values []string attrType = "string" if len(term.Inclusion.Values.Strings) > 0 { @@ -171,16 +171,16 @@ func (ExistsEvaluator) addTermConditions(term *ASTTerm, b *QueryBuilder) error { if term.Inclusion.Var == OwnerAttributeKey || term.Inclusion.Var == CreatorAttributeKey || term.Inclusion.Var == KeyAttributeKey { - values = append(values, b.pushArgument(strings.ToLower(value))) + values = append(values, b.PushArgument(strings.ToLower(value))) } else { - values = append(values, b.pushArgument(value)) + values = append(values, b.PushArgument(value)) } } } else { attrType = "numeric" values = make([]string, 0, len(term.Inclusion.Values.Numbers)) for _, value := range term.Inclusion.Values.Numbers { - values = append(values, b.pushArgument(value)) + values = append(values, b.PushArgument(value)) } } @@ -192,54 +192,54 @@ func (ExistsEvaluator) addTermConditions(term *ASTTerm, b *QueryBuilder) error { operation = "NOT IN" } } else if term.LessThan != nil { - key = b.pushArgument(term.LessThan.Var) + key = b.PushArgument(term.LessThan.Var) val := term.LessThan.Value if val.String != nil { attrType = "string" - value = b.pushArgument(*val.String) + value = b.PushArgument(*val.String) } else { attrType = "numeric" - value = b.pushArgument(*val.Number) + value = b.PushArgument(*val.Number) } operation = "<" } else if term.LessOrEqualThan != nil { - key = b.pushArgument(term.LessOrEqualThan.Var) + key = b.PushArgument(term.LessOrEqualThan.Var) val := term.LessOrEqualThan.Value if val.String != nil { attrType = "string" - value = b.pushArgument(*val.String) + value = b.PushArgument(*val.String) } else { attrType = "numeric" - value = b.pushArgument(*val.Number) + value = b.PushArgument(*val.Number) } operation = "<=" } else if term.GreaterThan != nil { - key = b.pushArgument(term.GreaterThan.Var) + key = b.PushArgument(term.GreaterThan.Var) val := term.GreaterThan.Value if val.String != nil { attrType = "string" - value = b.pushArgument(*val.String) + value = b.PushArgument(*val.String) } else { attrType = "numeric" - value = b.pushArgument(*val.Number) + value = b.PushArgument(*val.Number) } operation = ">" } else if term.GreaterOrEqualThan != nil { - key = b.pushArgument(term.GreaterOrEqualThan.Var) + key = b.PushArgument(term.GreaterOrEqualThan.Var) val := term.GreaterOrEqualThan.Value if val.String != nil { attrType = "string" - value = b.pushArgument(*val.String) + value = b.PushArgument(*val.String) } else { attrType = "numeric" - value = b.pushArgument(*val.Number) + value = b.PushArgument(*val.Number) } operation = ">=" } else if term.Glob != nil { - key = b.pushArgument(term.Glob.Var) + key = b.PushArgument(term.Glob.Var) val := term.Glob.Value attrType = "string" - value = b.pushArgument(val) + value = b.PushArgument(val) operation = "GLOB" if term.Glob.IsNot { diff --git a/query/query.go b/query/query.go index f08f2ec..0f86731 100644 --- a/query/query.go +++ b/query/query.go @@ -10,6 +10,16 @@ type SelectQuery struct { Args []any } +// Builder type to allow defining generic functions that can be re-used in other +// packages (like query-api) +type Builder interface { + PushArgument(any) string + WriteWhereClause(string) + GetOptions() *QueryOptions +} + +var _ Builder = &QueryBuilder{} + type QueryBuilder struct { queryBuilder *strings.Builder args []any @@ -26,7 +36,7 @@ func (b *QueryBuilder) nextTableName() string { return fmt.Sprintf("table_%d", b.tableCounter) } -func (b *QueryBuilder) pushArgument(arg any) string { +func (b *QueryBuilder) PushArgument(arg any) string { b.args = append(b.args, arg) b.argsCount += 1 return fmt.Sprintf("?%d", b.argsCount) @@ -40,19 +50,36 @@ func (b *QueryBuilder) writeComma() { } } -func (b *QueryBuilder) addPaginationArguments() error { +func (b *QueryBuilder) WriteWhereClause(s string) { + if b.needsWhere { + b.queryBuilder.WriteString(" WHERE ") + b.needsWhere = false + } else { + b.queryBuilder.WriteString(" AND ") + } + + b.queryBuilder.WriteString("(") + b.queryBuilder.WriteString(s) + b.queryBuilder.WriteString(")") +} + +func (b *QueryBuilder) GetOptions() *QueryOptions { + return &b.options +} + +func AddPaginationArguments(b Builder) error { paginationConditions := []string{} - if len(b.options.Cursor) > 0 { + if len(b.GetOptions().Cursor) > 0 { // Pre-allocate argument counters so that we don't need to duplicate them below - args := make([]string, 0, len(b.options.Cursor)) - for _, val := range b.options.Cursor { - args = append(args, b.pushArgument(val.Value)) + args := make([]string, 0, len(b.GetOptions().Cursor)) + for _, val := range b.GetOptions().Cursor { + args = append(args, b.PushArgument(val.Value)) } - for i := range b.options.Cursor { + for i := range b.GetOptions().Cursor { subcondition := []string{} - for j, from := range b.options.Cursor { + for j, from := range b.GetOptions().Cursor { if j > i { break } @@ -67,11 +94,11 @@ func (b *QueryBuilder) addPaginationArguments() error { arg := args[j] - columnIx, err := b.options.GetColumnIndex(from.ColumnName) + columnIx, err := b.GetOptions().GetColumnIndex(from.ColumnName) if err != nil { return fmt.Errorf("error getting column index: %w", err) } - column := b.options.Columns[columnIx] + column := b.GetOptions().Columns[columnIx] subcondition = append( subcondition, @@ -87,16 +114,7 @@ func (b *QueryBuilder) addPaginationArguments() error { paginationCondition := strings.Join(paginationConditions, " OR ") - if b.needsWhere { - b.queryBuilder.WriteString(" WHERE ") - b.needsWhere = false - } else { - b.queryBuilder.WriteString(" AND ") - } - - b.queryBuilder.WriteString("(") - b.queryBuilder.WriteString(paginationCondition) - b.queryBuilder.WriteString(")") + b.WriteWhereClause(paginationCondition) } return nil diff --git a/query/tables_method.go b/query/tables_method.go index b23b3e8..c5fec4e 100644 --- a/query/tables_method.go +++ b/query/tables_method.go @@ -65,7 +65,7 @@ func (e TablesEvaluator) EvaluateAST(ast *AST, options *QueryOptions) (*SelectQu sortingTable := fmt.Sprintf("arkiv_annotation_sorting%d", i) - keyPlaceholder := builder.pushArgument(orderBy.Name) + keyPlaceholder := builder.PushArgument(orderBy.Name) fmt.Fprintf(builder.queryBuilder, " LEFT JOIN %[1]s AS %s"+ @@ -79,7 +79,7 @@ func (e TablesEvaluator) EvaluateAST(ast *AST, options *QueryOptions) (*SelectQu ) } - err := builder.addPaginationArguments() + err := AddPaginationArguments(&builder) if err != nil { return nil, fmt.Errorf("error adding the pagination condition: %w", err) } @@ -91,7 +91,7 @@ func (e TablesEvaluator) EvaluateAST(ast *AST, options *QueryOptions) (*SelectQu builder.queryBuilder.WriteString(" AND ") } - blockArg := builder.pushArgument(builder.options.AtBlock) + blockArg := builder.PushArgument(builder.options.AtBlock) fmt.Fprintf(builder.queryBuilder, "%s BETWEEN e.from_block AND e.to_block - 1", blockArg) builder.queryBuilder.WriteString(" ORDER BY ") @@ -213,7 +213,7 @@ func (b *QueryBuilder) createAnnotationQuery( tableName = "numeric_attributes" } - blockArg := b.pushArgument(b.options.AtBlock) + blockArg := b.PushArgument(b.options.AtBlock) return b.createLeafQuery( strings.Join( @@ -231,8 +231,8 @@ func (b *QueryBuilder) createAnnotationQuery( } func (e *Glob) Evaluate(b *QueryBuilder) string { - varArg := b.pushArgument(e.Var) - valArg := b.pushArgument(e.Value) + varArg := b.PushArgument(e.Var) + valArg := b.PushArgument(e.Value) op := "GLOB" if e.IsNot { @@ -247,14 +247,14 @@ func (e *Glob) Evaluate(b *QueryBuilder) string { func (e *LessThan) Evaluate(b *QueryBuilder) string { attrType := "string" - varArg := b.pushArgument(e.Var) + varArg := b.PushArgument(e.Var) valArg := "" if e.Value.String != nil { - valArg = b.pushArgument(*e.Value.String) + valArg = b.PushArgument(*e.Value.String) } else { attrType = "numeric" - valArg = b.pushArgument(*e.Value.Number) + valArg = b.PushArgument(*e.Value.Number) } return b.createAnnotationQuery( @@ -265,14 +265,14 @@ func (e *LessThan) Evaluate(b *QueryBuilder) string { func (e *LessOrEqualThan) Evaluate(b *QueryBuilder) string { attrType := "string" - varArg := b.pushArgument(e.Var) + varArg := b.PushArgument(e.Var) valArg := "" if e.Value.String != nil { - valArg = b.pushArgument(*e.Value.String) + valArg = b.PushArgument(*e.Value.String) } else { attrType = "numeric" - valArg = b.pushArgument(*e.Value.Number) + valArg = b.PushArgument(*e.Value.Number) } return b.createAnnotationQuery( @@ -283,14 +283,14 @@ func (e *LessOrEqualThan) Evaluate(b *QueryBuilder) string { func (e *GreaterThan) Evaluate(b *QueryBuilder) string { attrType := "string" - varArg := b.pushArgument(e.Var) + varArg := b.PushArgument(e.Var) valArg := "" if e.Value.String != nil { - valArg = b.pushArgument(*e.Value.String) + valArg = b.PushArgument(*e.Value.String) } else { attrType = "numeric" - valArg = b.pushArgument(*e.Value.Number) + valArg = b.PushArgument(*e.Value.Number) } return b.createAnnotationQuery( @@ -301,14 +301,14 @@ func (e *GreaterThan) Evaluate(b *QueryBuilder) string { func (e *GreaterOrEqualThan) Evaluate(b *QueryBuilder) string { attrType := "string" - varArg := b.pushArgument(e.Var) + varArg := b.PushArgument(e.Var) valArg := "" if e.Value.String != nil { - valArg = b.pushArgument(*e.Value.String) + valArg = b.PushArgument(*e.Value.String) } else { attrType = "numeric" - valArg = b.pushArgument(*e.Value.Number) + valArg = b.PushArgument(*e.Value.Number) } return b.createAnnotationQuery( @@ -319,7 +319,7 @@ func (e *GreaterOrEqualThan) Evaluate(b *QueryBuilder) string { func (e *Equality) Evaluate(b *QueryBuilder) string { attrType := "string" - varArg := b.pushArgument(e.Var) + varArg := b.PushArgument(e.Var) valArg := "" op := "=" @@ -328,10 +328,10 @@ func (e *Equality) Evaluate(b *QueryBuilder) string { } if e.Value.String != nil { - valArg = b.pushArgument(*e.Value.String) + valArg = b.PushArgument(*e.Value.String) } else { attrType = "numeric" - valArg = b.pushArgument(*e.Value.Number) + valArg = b.PushArgument(*e.Value.Number) } return b.createAnnotationQuery( @@ -350,9 +350,9 @@ func (e *Inclusion) Evaluate(b *QueryBuilder) string { if e.Var == OwnerAttributeKey || e.Var == CreatorAttributeKey || e.Var == KeyAttributeKey { - values = append(values, b.pushArgument(strings.ToLower(value))) + values = append(values, b.PushArgument(strings.ToLower(value))) } else { - values = append(values, b.pushArgument(value)) + values = append(values, b.PushArgument(value)) } } @@ -360,7 +360,7 @@ func (e *Inclusion) Evaluate(b *QueryBuilder) string { attrType = "numeric" values = make([]string, 0, len(e.Values.Numbers)) for _, value := range e.Values.Numbers { - values = append(values, b.pushArgument(value)) + values = append(values, b.PushArgument(value)) } } @@ -371,7 +371,7 @@ func (e *Inclusion) Evaluate(b *QueryBuilder) string { condition = fmt.Sprintf("a.value NOT IN (%s)", paramStr) } - keyArg := b.pushArgument(e.Var) + keyArg := b.PushArgument(e.Var) return b.createAnnotationQuery( attrType,