mirror of
https://github.com/alm494/sql_proxy.git
synced 2026-04-22 19:33:55 +02:00
prepared statements
This commit is contained in:
+8
-11
@@ -153,7 +153,6 @@ func (o *DbList) Delete(id string) {
|
||||
func (o *DbList) PutPreparedStatement(id string, stmt *sql.Stmt) (string, bool) {
|
||||
val, ok := o.items.Load(id)
|
||||
if !ok {
|
||||
app.Log.Errorf("SQL connection with guid='%s' not found", id)
|
||||
return "", false
|
||||
}
|
||||
|
||||
@@ -170,15 +169,14 @@ func (o *DbList) PutPreparedStatement(id string, stmt *sql.Stmt) (string, bool)
|
||||
}
|
||||
|
||||
// Gets SQL prepared statement
|
||||
func (o *DbList) GetPreparedStatement(conn_id, stmt_id string) (*sql.Stmt, bool) {
|
||||
val, ok := o.items.Load(conn_id)
|
||||
func (o *DbList) GetPreparedStatement(connId, stmtId string) (*sql.Stmt, bool) {
|
||||
val, ok := o.items.Load(connId)
|
||||
if !ok {
|
||||
app.Log.Errorf("SQL connection with guid='%s' not found", conn_id)
|
||||
return nil, false
|
||||
}
|
||||
res := val.(*DbConn)
|
||||
for i := 0; i < len(res.Stmt); i++ {
|
||||
if res.Stmt[i].Id == stmt_id {
|
||||
for i := range res.Stmt {
|
||||
if res.Stmt[i].Id == stmtId {
|
||||
return res.Stmt[i].Stmt, true
|
||||
}
|
||||
}
|
||||
@@ -186,21 +184,20 @@ func (o *DbList) GetPreparedStatement(conn_id, stmt_id string) (*sql.Stmt, bool)
|
||||
}
|
||||
|
||||
// Closes and deletes SQL prepared statement
|
||||
func (o *DbList) ClosePreparedStatement(conn_id, stmt_id string) bool {
|
||||
val, ok := o.items.Load(conn_id)
|
||||
func (o *DbList) ClosePreparedStatement(connId, stmtId string) bool {
|
||||
val, ok := o.items.Load(connId)
|
||||
if !ok {
|
||||
app.Log.Errorf("SQL connection with guid='%s' not found", conn_id)
|
||||
return false
|
||||
}
|
||||
res := val.(*DbConn)
|
||||
for i := range res.Stmt {
|
||||
if res.Stmt[i].Id == stmt_id {
|
||||
if res.Stmt[i].Id == stmtId {
|
||||
res.Stmt[i].Stmt.Close()
|
||||
res.Stmt = slices.Delete(res.Stmt, i, i+1)
|
||||
break
|
||||
}
|
||||
}
|
||||
o.items.Store(conn_id, res)
|
||||
o.items.Store(connId, res)
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,9 @@ servers:
|
||||
- url: http://localhost/api/v1
|
||||
|
||||
paths:
|
||||
|
||||
/connection:
|
||||
|
||||
post:
|
||||
summary: Establish SQL connection
|
||||
description: First, check if an SQL connection has already been established. If not, create a new connection and add it to the application pool.
|
||||
@@ -53,6 +55,7 @@ paths:
|
||||
description: Bad request
|
||||
|
||||
/query:
|
||||
|
||||
get:
|
||||
summary: SELECT queries
|
||||
description: Use this method for any SQL query that is expected to return a result as a table, such as a SELECT statement. The resulting table is wrapped into a flexible JSON object, with columns dynamically determined based on the query.
|
||||
@@ -68,7 +71,7 @@ paths:
|
||||
name: SQL-Statement
|
||||
schema:
|
||||
type: string
|
||||
description: SQL statemet (url-encoded).
|
||||
description: SQL statement (url-encoded).
|
||||
required: true
|
||||
example: 'SELECT * FROM SALES WHERE Title LIKE "Manager %"'
|
||||
|
||||
@@ -102,7 +105,7 @@ paths:
|
||||
name: SQL-Statement
|
||||
schema:
|
||||
type: string
|
||||
description: SQL statemet (url-encoded).
|
||||
description: SQL statement (url-encoded).
|
||||
required: true
|
||||
example: 'DELETE FROM SALES WHERE id = 783'
|
||||
responses:
|
||||
@@ -112,6 +115,69 @@ paths:
|
||||
description: Bad request
|
||||
'403':
|
||||
description: Forbidden
|
||||
|
||||
/prepared:
|
||||
|
||||
post:
|
||||
summary: Create prepared statement
|
||||
description: 'Create prepared statement'
|
||||
parameters:
|
||||
- in: header
|
||||
name: Connection-Id
|
||||
schema:
|
||||
type: string
|
||||
description: SQL connection id as GUID in a plain text, must be obtained by /connection POST method.
|
||||
required: true
|
||||
example: '52f0b434-4eae-4cc6-803c-2d2f604fe16c'
|
||||
- in: header
|
||||
name: Prepared-Statement
|
||||
schema:
|
||||
type: string
|
||||
description: SQL prepared statement with parameters, both for select or execute (url-encoded).
|
||||
required: true
|
||||
example: 'SELECT * SALES WHERE id = ? and name = ?'
|
||||
responses:
|
||||
'200':
|
||||
description: OK
|
||||
content:
|
||||
text/plain:
|
||||
schema:
|
||||
type: string
|
||||
description: return SQL prepared statement id as GUID in a plain text.
|
||||
example: 'f3f0b434-e4ae-c4c6-c803-d22f504fe16c'
|
||||
'400':
|
||||
description: Bad request
|
||||
'403':
|
||||
description: Forbidden
|
||||
'500':
|
||||
description: Internal server error
|
||||
|
||||
delete:
|
||||
summary: Close prepared statement
|
||||
description: Close and delete prepared statement.
|
||||
parameters:
|
||||
- in: header
|
||||
name: Connection-Id
|
||||
schema:
|
||||
type: string
|
||||
description: SQL connection id as GUID in a plain text.
|
||||
required: true
|
||||
example: '52f0b434-4eae-4cc6-803c-2d2f604fe16c'
|
||||
- in: header
|
||||
name: Statement-Id
|
||||
schema:
|
||||
type: string
|
||||
description: Prepared statement id as GUID in a plain text.
|
||||
required: true
|
||||
example: 'f3f0b434-e4ae-c4c6-c803-d22f504fe16c'
|
||||
|
||||
responses:
|
||||
'200':
|
||||
description: OK
|
||||
'400':
|
||||
description: Bad request
|
||||
'403':
|
||||
description: Forbidden
|
||||
|
||||
components:
|
||||
schemas:
|
||||
|
||||
@@ -6,7 +6,9 @@ servers:
|
||||
- url: http://localhost/api/v1
|
||||
|
||||
paths:
|
||||
|
||||
/connection:
|
||||
|
||||
post:
|
||||
summary: Получить SQL-соединение
|
||||
description: Сначала проверяет, установлено ли уже SQL-соединение. Если нет, устанавливает новое и добавляет его в пул доступных соединений.
|
||||
@@ -53,6 +55,7 @@ paths:
|
||||
description: Неверный запрос
|
||||
|
||||
/query:
|
||||
|
||||
get:
|
||||
summary: Запросы SELECT
|
||||
description: Используйте этот метод для любого SQL-запроса, от который ожидается результат в виде таблицы, например, с оператором SELECT. Полученная таблица оборачивается в JSON-объект, с колонками, определяемыми динамически на основе запроса.
|
||||
@@ -112,6 +115,69 @@ paths:
|
||||
description: Неверный запрос
|
||||
'403':
|
||||
description: Запрещено
|
||||
|
||||
/prepared:
|
||||
|
||||
post:
|
||||
summary: Создать предварительно подготовленный оператор
|
||||
description: Используйте этот метод для создания предварительно подготовленного оператора
|
||||
parameters:
|
||||
- in: header
|
||||
name: Connection-Id
|
||||
schema:
|
||||
type: string
|
||||
description: Идентификатор SQL-соединения в форме GUID, должен быть получен ранее методом POST /connection.
|
||||
required: true
|
||||
example: '52f0b434-4eae-4cc6-803c-2d2f604fe16c'
|
||||
- in: header
|
||||
name: Prepared-Statement
|
||||
description: Предварительно подготовленный оператор SQL с параметрами, для выборки или изменения данных (url-кодированный).
|
||||
schema:
|
||||
type: string
|
||||
required: true
|
||||
example: 'SELECT * SALES WHERE id = ? and name = ?'
|
||||
responses:
|
||||
'200':
|
||||
description: OK
|
||||
content:
|
||||
text/plain:
|
||||
schema:
|
||||
type: string
|
||||
description: Возвращает идентификатор подготовленного оператора SQL в форме GUID..
|
||||
example: 'f3f0b434-e4ae-c4c6-c803-d22f504fe16c'
|
||||
'400':
|
||||
description: Неверный запрос
|
||||
'403':
|
||||
description: Запрещено
|
||||
'500':
|
||||
description: Внутренняя ошибка сервера
|
||||
|
||||
delete:
|
||||
summary: Закрыть предварительно подготовленный оператор
|
||||
description: Закрыть и удалить предварительно подготовленный оператор SQL.
|
||||
parameters:
|
||||
- in: header
|
||||
name: Connection-Id
|
||||
schema:
|
||||
type: string
|
||||
description: Идентификатор SQL-соединения в форме GUID, должен быть получен ранее методом POST /connection.
|
||||
required: true
|
||||
example: '52f0b434-4eae-4cc6-803c-2d2f604fe16c'
|
||||
- in: header
|
||||
name: Statement-Id
|
||||
schema:
|
||||
type: string
|
||||
description: Идентификатор предварительно подготовленного оператора SQL в форме GUID, должен быть получен ранее методом POST /prepared.
|
||||
required: true
|
||||
example: 'f3f0b434-e4ae-c4c6-c803-d22f504fe16c'
|
||||
|
||||
responses:
|
||||
'200':
|
||||
description: OK
|
||||
'400':
|
||||
description: Неверный запрос
|
||||
'403':
|
||||
description: Запрещено
|
||||
|
||||
components:
|
||||
schemas:
|
||||
|
||||
@@ -16,11 +16,10 @@ func CreateConnection(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
if connGuid, ok := db.Handler.GetByParams(&dbConnInfo); !ok {
|
||||
errorResponce(w, "Failed to get SQL connection", http.StatusInternalServerError)
|
||||
} else {
|
||||
if _, err := w.Write([]byte(connGuid)); err != nil {
|
||||
errorResponce(w, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
} else if _, err := w.Write([]byte(connGuid)); err != nil {
|
||||
errorResponce(w, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func CloseConnection(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
+39
-18
@@ -1,58 +1,79 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"sql-proxy/src/app"
|
||||
"sql-proxy/src/db"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func PrepareStatement(w http.ResponseWriter, r *http.Request) {
|
||||
var requestBody map[string]any
|
||||
|
||||
if err := json.NewDecoder(r.Body).Decode(&requestBody); err != nil {
|
||||
errorResponce(w, "Error decoding JSON", http.StatusBadRequest)
|
||||
connId := r.Header.Get("Connection-Id")
|
||||
preparedStatement, err := url.QueryUnescape(r.Header.Get("Prepared-Statement"))
|
||||
|
||||
if err != nil || connId == "" || preparedStatement == "" {
|
||||
errorResponce(w, "Bad request", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
defer r.Body.Close()
|
||||
app.Log.WithFields(logrus.Fields{
|
||||
"prepared_statement": preparedStatement,
|
||||
"connection_id": connId,
|
||||
}).Debug("Prepare statement received:")
|
||||
|
||||
conn, ok := db.Handler.GetById(requestBody["connection_id"].(string), true)
|
||||
conn, ok := db.Handler.GetById(connId, true)
|
||||
if !ok {
|
||||
errorResponce(w, "Invalid connection id", http.StatusBadRequest)
|
||||
errorResponce(w, "Invalid connection id", http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
stmt, err := conn.Prepare(requestBody["sql"].(string))
|
||||
stmt, err := conn.Prepare(preparedStatement)
|
||||
if err != nil {
|
||||
errorResponce(w, err.Error(), http.StatusBadRequest)
|
||||
}
|
||||
|
||||
stmt_id, ok := db.Handler.PutPreparedStatement(requestBody["connection_id"].(string), stmt)
|
||||
stmtId, ok := db.Handler.PutPreparedStatement(connId, stmt)
|
||||
if !ok {
|
||||
errorResponce(w, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
if _, err = w.Write([]byte(stmt_id)); err != nil {
|
||||
if _, err = w.Write([]byte(stmtId)); err != nil {
|
||||
errorResponce(w, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func PreparedSelect(w http.ResponseWriter, r *http.Request) {
|
||||
var requestBody map[string]any
|
||||
|
||||
if err := json.NewDecoder(r.Body).Decode(&requestBody); err != nil {
|
||||
errorResponce(w, "Error decoding JSON", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
defer r.Body.Close()
|
||||
|
||||
// to do
|
||||
|
||||
}
|
||||
|
||||
func PreparedExecute(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// to do
|
||||
}
|
||||
|
||||
func ClosePreparedStatement(w http.ResponseWriter, r *http.Request) {
|
||||
// to do
|
||||
|
||||
connId := r.Header.Get("Connection-Id")
|
||||
stmtId := r.Header.Get("Statement-Id")
|
||||
|
||||
if connId == "" || stmtId == "" {
|
||||
errorResponce(w, "Bad request", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
app.Log.WithFields(logrus.Fields{
|
||||
"connection_id": connId,
|
||||
"prepared_statement": stmtId,
|
||||
}).Debug("Delete prepared statememt received:")
|
||||
|
||||
if ok := db.Handler.ClosePreparedStatement(connId, stmtId); !ok {
|
||||
errorResponce(w, "Forbidden", http.StatusForbidden)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -13,21 +13,21 @@ import (
|
||||
|
||||
func SelectQuery(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
conn_id := r.Header.Get("Connection-Id")
|
||||
connId := r.Header.Get("Connection-Id")
|
||||
query, err := url.QueryUnescape(r.Header.Get("SQL-Statement"))
|
||||
|
||||
if err != nil || conn_id == "" || query == "" {
|
||||
if err != nil || connId == "" || query == "" {
|
||||
errorResponce(w, "Bad request", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
app.Log.WithFields(logrus.Fields{
|
||||
"sql": query,
|
||||
"connection_id": conn_id,
|
||||
"connection_id": connId,
|
||||
}).Debug("SQL query received:")
|
||||
|
||||
// Search existings connection in the pool
|
||||
dbConn, ok := db.Handler.GetById(conn_id, true)
|
||||
dbConn, ok := db.Handler.GetById(connId, true)
|
||||
if !ok {
|
||||
errorResponce(w, "Failed to get SQL connection", http.StatusForbidden)
|
||||
return
|
||||
@@ -59,20 +59,20 @@ func SelectQuery(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
func ExecuteQuery(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
conn_id := r.Header.Get("Connection-Id")
|
||||
connId := r.Header.Get("Connection-Id")
|
||||
query, err := url.QueryUnescape(r.Header.Get("SQL-Statement"))
|
||||
|
||||
if err != nil || conn_id == "" || query == "" {
|
||||
if err != nil || connId == "" || query == "" {
|
||||
errorResponce(w, "Bad request", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
app.Log.WithFields(logrus.Fields{
|
||||
"sql": query,
|
||||
"connection_id": conn_id,
|
||||
"connection_id": connId,
|
||||
}).Debug("SQL query received:")
|
||||
|
||||
dbConn, ok := db.Handler.GetById(conn_id, true)
|
||||
dbConn, ok := db.Handler.GetById(connId, true)
|
||||
if !ok {
|
||||
errorResponce(w, "Invalid connection id", http.StatusForbidden)
|
||||
return
|
||||
@@ -80,7 +80,7 @@ func ExecuteQuery(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
app.Log.WithFields(logrus.Fields{
|
||||
"sql": query,
|
||||
"connection_id": conn_id,
|
||||
"connection_id": connId,
|
||||
}).Debug("SQL execute query received:")
|
||||
|
||||
_, err = dbConn.Exec(query)
|
||||
|
||||
Reference in New Issue
Block a user