Go Query Builder
This challenge asks you to implement a basic SQL query builder in Go. A query builder is a useful tool for programmatically constructing SQL queries, which can help prevent SQL injection vulnerabilities and make query construction more readable and maintainable, especially for complex queries.
Problem Description
You need to design and implement a QueryBuilder struct in Go that allows users to construct a SQL SELECT statement. The builder should support adding SELECT fields, specifying a FROM table, and adding WHERE clauses. The goal is to produce a well-formed SQL string from the builder's configuration.
Key Requirements:
NewQueryBuilder(): A constructor function that returns an initializedQueryBuilder.Select(fields ...string): A method to specify the columns to select. It should accept a variable number of string arguments representing column names. IfSelectis called multiple times, it should append to the existing selection. If no fields are provided, it should default to selecting all columns (*).From(table string): A method to specify the table to query from. This method should be called only once.Where(field string, operator string, value interface{}): A method to add aWHEREclause.field: The column name.operator: The comparison operator (e.g.,=,>,<,>=,<=,!=,LIKE).value: The value to compare against. This can be a string, integer, float, boolean, ornil.- The builder should handle parameterization for
WHEREclause values to prevent SQL injection. You can represent parameterized values using placeholders like?. - If
Whereis called multiple times, subsequent clauses should be joined byAND.
Build(): A method that returns the generated SQL query string and a slice of the corresponding values for the parameterizedWHEREclauses. If the builder is invalid (e.g.,Fromnot called), it should return an error.
Expected Behavior:
- The generated SQL should be correctly formatted and syntactically valid.
WHEREclause values should be properly escaped or parameterized.- The order of operations in building the query should be logical (
SELECTthenFROMthenWHERE).
Edge Cases to Consider:
- Calling
Build()beforeFrom(). - Calling
Select()with no arguments. - Adding
WHEREclauses withnilvalues (should translate toIS NULL). - Handling different data types for
WHEREclause values.
Examples
Example 1: Basic Select with Where Clause
Input:
builder := NewQueryBuilder()
builder.Select("name", "email")
builder.From("users")
builder.Where("id", "=", 123)
query, values := builder.Build()
Output:
Query: "SELECT name, email FROM users WHERE id = ?"
Values: [123]
Explanation:
The query selects "name" and "email" from the "users" table. A WHERE clause filters by "id" equal to 123, represented by a parameterized placeholder.
Example 2: Multiple Where Clauses and Different Operators
Input:
builder := NewQueryBuilder()
builder.Select("product_name", "price")
builder.From("products")
builder.Where("category", "=", "electronics")
builder.Where("price", ">", 500.00)
builder.Where("in_stock", "=", true)
query, values := builder.Build()
Output:
Query: "SELECT product_name, price FROM products WHERE category = ? AND price > ? AND in_stock = ?"
Values: ["electronics", 500.00, true]
Explanation:
This query selects product details from "products" and applies three WHERE conditions joined by "AND". All values are parameterized.
Example 3: Default Select All and NULL Value
Input:
builder := NewQueryBuilder()
builder.From("orders")
builder.Where("shipped_date", "=", nil)
query, values := builder.Build()
Output:
Query: "SELECT * FROM orders WHERE shipped_date IS NULL"
Values: []
Explanation:
When no fields are specified in `Select`, it defaults to `*`. A `nil` value in the WHERE clause correctly translates to `IS NULL`.
Example 4: Multiple Select Calls
Input:
builder := NewQueryBuilder()
builder.Select("first_name")
builder.Select("last_name")
builder.From("customers")
query, values := builder.Build()
Output:
Query: "SELECT first_name, last_name FROM customers"
Values: []
Explanation:
Calling `Select` multiple times appends to the list of selected fields.
Constraints
- The
QueryBuildershould support standard SQL data types forWHEREclause values:string,int,float64,bool, andnil. - The builder should handle up to 100
WHEREclauses efficiently. - The generated SQL string should not exceed 1024 characters.
- The
Build()method must return anerrorifFrom()has not been called.
Notes
- Consider how to store the selected fields, table name, and WHERE clauses within the
QueryBuilderstruct. - You will need to manage the generation of placeholders for the
WHEREclause values. - Think about how to handle different types of values when constructing the SQL string and the values slice. For example, how would you represent a boolean
truein SQL? - The
interface{}type in Go is useful for accepting a wide range of value types in theWheremethod. - For
WHEREclauses, ensure that string values are enclosed in single quotes in the final SQL, unless they are part of a placeholder. The builder should abstract this away.