Skip to content
This repository was archived by the owner on Mar 4, 2025. It is now read-only.

Commit 7cedd3c

Browse files
committed
api, common, live: Now returns the number of rows changed by API Execute() call
1 parent 567b380 commit 7cedd3c

File tree

8 files changed

+94
-99
lines changed

8 files changed

+94
-99
lines changed

api/handlers.go

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -372,17 +372,18 @@ func deleteHandler(w http.ResponseWriter, r *http.Request) {
372372
return
373373
}
374374

375-
// Delete the database from Minio too
376-
bucket := fmt.Sprintf("live-%s", dbOwner)
377-
id := dbName
378-
err = com.MinioDeleteDatabase("API server", dbOwner, dbName, bucket, id)
379-
if err != nil {
380-
jsonErr(w, err.Error(), http.StatusInternalServerError)
381-
return
382-
}
383-
384-
// For a live database, tell our AMQP backend to delete the database file
375+
// For a live database, delete it from both Minio and our AMQP backend
385376
if isLive {
377+
// Delete the database from Minio
378+
bucket := fmt.Sprintf("live-%s", dbOwner)
379+
id := dbName
380+
err = com.MinioDeleteDatabase("API server", dbOwner, dbName, bucket, id)
381+
if err != nil {
382+
jsonErr(w, err.Error(), http.StatusInternalServerError)
383+
return
384+
}
385+
386+
// Delete the database from our AMQP backend
386387
var rawResponse []byte
387388
rawResponse, err = com.MQSendRequest(com.AmqpChan, liveNode, "delete", loggedInUser, dbOwner, dbName, "")
388389
if err != nil {
@@ -668,9 +669,9 @@ func executeHandler(w http.ResponseWriter, r *http.Request) {
668669
return
669670
}
670671

671-
// Reject attempts to run Exec() on non-live databases
672+
// Reject attempts to run Execute() on non-live databases
672673
if !isLive {
673-
jsonErr(w, "Exec() only runs on Live databases, which this isn't.", http.StatusInternalServerError)
674+
jsonErr(w, "Execute() only runs on Live databases. This is not a live database.", http.StatusInternalServerError)
674675
return
675676
}
676677

@@ -687,28 +688,31 @@ func executeHandler(w http.ResponseWriter, r *http.Request) {
687688

688689
// Send the query execution request to our AMQP backend
689690
var rawResponse []byte
690-
rawResponse, err = com.MQSendRequest(com.AmqpChan, liveNode, "exec", loggedInUser, dbOwner, dbName, query)
691+
rawResponse, err = com.MQSendRequest(com.AmqpChan, liveNode, "execute", loggedInUser, dbOwner, dbName, query)
691692
if err != nil {
692693
return
693694
}
694695

695696
// Decode the response
696-
var resp com.LiveDBErrorResponse
697+
var resp com.LiveDBExecuteResponse
697698
err = json.Unmarshal(rawResponse, &resp)
698699
if err != nil {
699700
log.Println(err)
701+
jsonErr(w, err.Error(), http.StatusInternalServerError)
700702
return
701703
}
704+
705+
// If the Execute() failed, then provide the error message to the user
702706
if resp.Error != "" {
703-
err = errors.New(resp.Error)
707+
jsonErr(w, resp.Error, http.StatusBadRequest)
704708
return
705709
}
706710

707-
// Return a "success" message
708-
z := com.StatusResponseContainer{Status: "OK"}
711+
// The Execute() succeeded, so pass along the # of rows changed
712+
z := com.ExecuteResponseContainer{RowsChanged: resp.RowsChanged, Status: "OK"}
709713
jsonData, err := json.MarshalIndent(z, "", " ")
710714
if err != nil {
711-
log.Printf("Error when JSON marshalling returned data in execHandler(): %v\n", err)
715+
log.Printf("Error when JSON marshalling returned data in executeHandler(): %v\n", err)
712716
jsonErr(w, err.Error(), http.StatusInternalServerError)
713717
return
714718
}

api/templates/root.html

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@
5858
<div class="col-md-12"><a href="https://dbhub.io"><h1>DBHub API (in early development)</h1></a></div>
5959
</div>
6060
<div class="row">
61-
<div class="col-md-12 version">API version 0.2, last modified 2023-03-22</div>
61+
<div class="col-md-12 version">API version 0.3, last modified 2023-03-26</div>
6262
</div>
6363
<div class="panel panel-primary" id="note">
6464
<div class="panel-heading heading">Note</div>
@@ -98,7 +98,7 @@
9898
<li class="list-group-item"><a href="#delete" class="apiheading">Delete</a> - Deletes a database from the requesting users account</li>
9999
<li class="list-group-item"><a href="#diff" class="apiheading">Diff</a> - Generates a diff between two databases or two versions of a database</li>
100100
<li class="list-group-item"><a href="#download" class="apiheading">Download</a> - Returns the requested SQLite database file</li>
101-
<li class="list-group-item" style="color: #a01e1a"><a href="#execute" class="apiheading" style="color: #a01e1a">Execute</a> - Executes a SQLite statement on a LIVE database <span style="font-style: italic">(new in version 0.2)</span> - <span style="font-weight: bold">EXPERIMENTAL ONLY</span></li>
101+
<li class="list-group-item" style="color: #a01e1a"><a href="#execute" class="apiheading" style="color: #a01e1a">Execute</a> - Executes a SQLite statement on a LIVE database <span style="font-style: italic">(new in version 0.2, updated in version 0.3)</span> - <span style="font-weight: bold">EXPERIMENTAL ONLY</span></li>
102102
<li class="list-group-item"><a href="#indexes" class="apiheading">Indexes</a> - Returns the details of all indexes in a SQLite database</li>
103103
<li class="list-group-item"><a href="#metadata" class="apiheading">Metadata</a> - Returns the commit, branch, release, tag and web page information for a database</li>
104104
<li class="list-group-item"><a href="#query" class="apiheading">Query</a> - Runs a SQLite SELECT query on a database</li>
@@ -568,11 +568,11 @@
568568
<div class="col-md-2">Required</div>
569569
<div class="col-md-7">Your API key. These can be generated in your <a href="https://[[ .ServerName ]]/pref">Settings</a> page, when logged in</div>
570570
</div>
571-
<div class="row indent">
572-
<div class="col-md-2 paramname" style="color: #a01e1a">live</div>
573-
<div class="col-md-1 type" style="color: #a01e1a">boolean</div>
574-
<div class="col-md-2" style="color: #a01e1a">Optional</div>
575-
<div class="col-md-7" style="color: #a01e1a"><div style="font-style: italic; font-weight: bold">NEW in API version 0.2</div><div>A boolean string ("true", "false") to switch between returning the list of standard databases, and the list of live databases</div><div>NOTE: This "live" parameter is experimental, and may change</div></div>
571+
<div class="row indent" style="color: #a01e1a">
572+
<div class="col-md-2 paramname">live</div>
573+
<div class="col-md-1 type">boolean</div>
574+
<div class="col-md-2">Optional</div>
575+
<div class="col-md-7"><div style="font-style: italic; font-weight: bold">NEW in API version 0.2</div><div>A boolean string ("true", "false") to switch between returning the list of standard databases, and the list of live databases</div><div>NOTE: This "live" parameter is experimental, and may change</div></div>
576576
</div>
577577
<div class="row">
578578
<div class="col-md-2 heading pad">Return values (JSON)</div>
@@ -813,7 +813,7 @@
813813

814814
<!-- Execute -->
815815
<div class="panel panel-default" id="execute">
816-
<div class="panel-heading heading" style="color: #a01e1a;"><span style="font-size: x-large;">Execute</span> &nbsp; (new in version 0.2)</div>
816+
<div class="panel-heading heading" style="color: #a01e1a;"><span style="font-size: x-large;">Execute</span> &nbsp; (new in version 0.2, updated in version 0.3)</div>
817817
<div class="panel-body">
818818
<div class="row">
819819
<div class="col-md-11 heading" style="color: #a01e1a; padding-bottom: 10px;">This function is EXPERIMENTAL, and may change</div>
@@ -861,6 +861,11 @@
861861
<div class="col-md-1">Type</div>
862862
<div class="col-md-7">Description</div>
863863
</div>
864+
<div class="row indent" style="color: #a01e1a">
865+
<div class="col-md-4 paramname">rows_changed</div>
866+
<div class="col-md-1 type">integer</div>
867+
<div class="col-md-7"><div style="font-style: italic; font-weight: bold">NEW in API version 0.3</div><div>Contain the number of rows changed by the Execute() call</div></div>
868+
</div>
864869
<div class="row indent">
865870
<div class="col-md-4 paramname">status</div>
866871
<div class="col-md-1 type">string</div>
@@ -880,10 +885,12 @@
880885
https://api.dbhub.io/v1/execute</pre>
881886
Output:
882887
<pre>{
888+
"rows_changed": 1,
883889
"status": "OK"
884890
}</pre>
885891
<span style="color: darkred; font-size: larger; font-weight: bolder;">Note</span> - This is a newly added EXPERIMENTAL function, that only works with
886-
LIVE databases. To try it out, <a href="https://dbhub.io/upload/">Upload</a> a new database and
892+
LIVE databases. To try it out <a href="#upload">Upload</a> a new "LIVE" database using this API,
893+
or use the <a href="https://dbhub.io/upload/">Upload</a> page on the main DBHub.io website and
887894
select the LIVE option under "Advanced".
888895
</div>
889896
</div>
@@ -1625,11 +1632,11 @@
16251632
<div class="col-md-2">Optional</div>
16261633
<div class="col-md-7">The sha256 (a unique identifier) of the licence for the database (up to this commit)</div>
16271634
</div>
1628-
<div class="row indent">
1629-
<div class="col-md-2 paramname" style="color: #a01e1a">live</div>
1630-
<div class="col-md-1 type" style="color: #a01e1a">boolean</div>
1631-
<div class="col-md-2" style="color: #a01e1a">Optional</div>
1632-
<div class="col-md-7" style="color: #a01e1a"><div style="font-style: italic; font-weight: bold">NEW in API version 0.2</div><div>A boolean string ("true", "false") indicating whether this upload is a live database</div><div>NOTE: This "live" parameter is experimental, and may change</div></div>
1635+
<div class="row indent" style="color: #a01e1a">
1636+
<div class="col-md-2 paramname">live</div>
1637+
<div class="col-md-1 type">boolean</div>
1638+
<div class="col-md-2">Optional</div>
1639+
<div class="col-md-7"><div style="font-style: italic; font-weight: bold">NEW in API version 0.2</div><div>A boolean string ("true", "false") indicating whether this upload is a live database</div><div>NOTE: This "live" parameter is experimental, and may change</div></div>
16331640
</div>
16341641
<div class="row indent">
16351642
<div class="col-md-2 paramname">otherparents</div>

common/live.go

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -33,20 +33,27 @@ type LiveDBColumnsResponse struct {
3333
Error string `json:"error"`
3434
}
3535

36+
// LiveDBErrorResponse holds just the node name and any error message used in responses by our AMQP backend
37+
// It's useful for error message, and other responses where no other fields are needed
38+
type LiveDBErrorResponse struct {
39+
Node string `json:"node"`
40+
Error string `json:"error"`
41+
}
42+
43+
// LiveDBExecuteResponse returns the number of rows changed by an Execute() call
44+
type LiveDBExecuteResponse struct {
45+
Node string `json:"node"`
46+
RowsChanged int `json:"rows_changed"`
47+
Error string `json:"error"`
48+
}
49+
3650
// LiveDBIndexesResponse holds the fields used for receiving index list responses from our AMQP backend
3751
type LiveDBIndexesResponse struct {
3852
Node string `json:"node"`
3953
Indexes []APIJSONIndex `json:"indexes"`
4054
Error string `json:"error"`
4155
}
4256

43-
// LiveDBErrorResponse holds just the node name and any error message used in responsed by our AMQP backend
44-
// It's useful for error message, and other responses where no other fields are needed
45-
type LiveDBErrorResponse struct {
46-
Node string `json:"node"`
47-
Error string `json:"error"`
48-
}
49-
5057
// LiveDBQueryResponse holds the fields used for receiving query responses from our AMQP backend
5158
type LiveDBQueryResponse struct {
5259
Node string `json:"node"`
@@ -358,12 +365,13 @@ func MQDeleteResponse(msg amqp.Delivery, channel *amqp.Channel, nodeName string,
358365
return
359366
}
360367

361-
// MQExecResponse sends a message in response to an AMQP database query exec request
362-
func MQExecResponse(msg amqp.Delivery, channel *amqp.Channel, nodeName string, errMsg string) (err error) {
368+
// MQExecuteResponse sends a message in response to an AMQP database execute request
369+
func MQExecuteResponse(msg amqp.Delivery, channel *amqp.Channel, nodeName string, rowsChanged int, errMsg string) (err error) {
363370
// Construct the response
364-
resp := LiveDBErrorResponse{ // Yep, we're reusing this super basic error response instead of creating a new one
365-
Node: nodeName,
366-
Error: errMsg,
371+
resp := LiveDBExecuteResponse{
372+
Node: nodeName,
373+
RowsChanged: rowsChanged,
374+
Error: errMsg,
367375
}
368376
var z []byte
369377
z, err = json.Marshal(resp)
@@ -386,7 +394,7 @@ func MQExecResponse(msg amqp.Delivery, channel *amqp.Channel, nodeName string, e
386394
}
387395
msg.Ack(false)
388396
if AmqpDebug {
389-
log.Printf("[EXEC] Live node '%s' responded with ACK to message with correlationID: '%s', msg.ReplyTo: '%s'", nodeName, msg.CorrelationId, msg.ReplyTo)
397+
log.Printf("[EXECUTE] Live node '%s' responded with ACK to message with correlationID: '%s', msg.ReplyTo: '%s'", nodeName, msg.CorrelationId, msg.ReplyTo)
390398
}
391399
return
392400
}

common/postgresql.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -836,8 +836,8 @@ func DeleteComment(dbOwner, dbFolder, dbName string, discID, comID int) error {
836836

837837
// DeleteDatabase deletes a database from PostgreSQL
838838
func DeleteDatabase(dbOwner, dbFolder, dbName string) error {
839-
// TODO: At some point we'll need to figure out a garbage collection approach to remove databases from Minio which
840-
// TODO are no longer pointed to by anything
839+
// TODO: At some point we'll need to figure out a garbage collection approach to remove standard databases
840+
// from Minio which are no longer pointed to by anything
841841

842842
// Is this a live database
843843
isLive, _, err := CheckDBLive(dbOwner, dbFolder, dbName)

common/responses.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,14 @@ func BranchListResponse(dbOwner, dbFolder, dbName string) (list BranchListRespon
3636
return
3737
}
3838

39+
// ExecuteResponseContainer is used by our AMQP backend, to return information in response to an
40+
// Execute() call on a live database. It holds the success/failure status of the remote call,
41+
// and also the number of rows changed by the Execute() call (if it succeeded)
42+
type ExecuteResponseContainer struct {
43+
RowsChanged int `json:"rows_changed"`
44+
Status string `json:"status"`
45+
}
46+
3947
// MetadataResponseContainer holds the response to a client request for database metadata. It's a temporary structure,
4048
// mainly so the JSON created for it is consistent between our various daemons
4149
type MetadataResponseContainer struct {

common/sqlite.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -814,8 +814,8 @@ func ReadSQLiteDBCSV(sdb *sqlite.Conn, dbTable string) ([][]string, error) {
814814
return resultSet, nil
815815
}
816816

817-
// SQLiteExecQueryLive is used by our AMQP backend infrastructure to execute a user provided SQLite query
818-
func SQLiteExecQueryLive(baseDir, dbOwner, dbName, loggedInUser, query string) (err error) {
817+
// SQLiteExecuteQueryLive is used by our AMQP backend infrastructure to execute a user provided SQLite statement
818+
func SQLiteExecuteQueryLive(baseDir, dbOwner, dbName, loggedInUser, query string) (rowsChanged int, err error) {
819819
// Open the Live database on the local node
820820
var sdb *sqlite.Conn
821821
sdb, err = OpenSQLiteDatabaseLive(baseDir, dbOwner, dbName)
@@ -838,7 +838,7 @@ func SQLiteExecQueryLive(baseDir, dbOwner, dbName, loggedInUser, query string) (
838838
defer stmt.Finalize()
839839

840840
// Execute the statement
841-
err = stmt.Exec()
841+
rowsChanged, err = stmt.ExecDml()
842842
if err != nil {
843843
log.Printf("Error when executing query by '%s' for LIVE database (%s/%s): '%s'\n",
844844
SanitiseLogString(loggedInUser), SanitiseLogString(dbOwner), SanitiseLogString(dbName),

docker/README.md

Lines changed: 9 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -4,61 +4,25 @@ This is a docker image for running the DBHub.io daemons, so we can test PR's
44
automatically. And eventually, probably automatically test DB4S communication
55
with them too.
66

7-
It includes the three DBHub.io daemons:
7+
It includes the four DBHub.io daemons:
88

9-
* The webUI, listening on port 8080
9+
* The webUI, listening on port 9443
10+
* The REST API end point, listening on port 9444
1011
* The DB4S end point (the daemon DB Browser for SQLite talks to) on port 5550
11-
* The REST API end point, listening on port 8444
12+
* The internal-use-only "live" database daemon,
1213

1314
...and the dependencies for the daemons:
1415

1516
* PostgreSQL
1617
* Memcached
1718
* Minio
19+
* RabbitMQ
1820

1921
This is done as an all-in-one image for now. It _might_ be better separated
2022
into separate services per damon (eg for docker-compose), but that'll be a
2123
later thing (if needed).
2224

2325

24-
## How to use
25-
26-
To build this image yourself, from this "docker" subdirectory type:
27-
28-
$ docker build .
29-
30-
That should generate the image successfully, and give an image ID on the
31-
final line.
32-
33-
To run the image, use that image ID with the following command:
34-
35-
$ docker run -it --rm <image ID>
36-
37-
This will place you in an ash command shell, running as root in a container
38-
of the image. There are two scripts worth knowing about:
39-
40-
* /usr/local/bin/init.sh
41-
* /usr/local/bin/start.sh
42-
43-
The init.sh initialises Minio and the PostgreSQL database, then loads the
44-
DBHub.io schema into the database.
45-
46-
You'll want to run this before running start.sh, which starts all of the
47-
daemons (Memcached, Minio, PostgreSQL, DBHub.io webUI, DBHub.io DB4S end
48-
point.
49-
50-
If you want to keep the PostgreSQL and Minio data between sessions, you'll
51-
need to mount the /data directory in the container to somewhere on your local
52-
pc. Do this by starting the docker container with this line instead:
53-
54-
$ docker run -it --rm -v /some/diretory/on/your/pc:/data <image ID>
55-
56-
This time, when the init.sh script is run, it will create the PostgreSQL
57-
database + Minio structures in that folder on your disk. Using the same
58-
location between sessions will persist the data across all of those
59-
sessions.
60-
61-
6226
## Server name
6327

6428
The (self signed) certificate authority and associated certs in the certs/
@@ -68,7 +32,10 @@ local desktops /etc/hosts, pointing at the running docker image.
6832

6933
This way, when your web browser (Firefox, etc) tries to visit:
7034

71-
https://docker-dev.dbhub.io:8443
35+
https://docker-dev.dbhub.io:9443
7236

7337
... it will go to the docker image, using the name expected by the
7438
server certificate. Having the name match correctly is useful.
39+
40+
Or just use "localhost" as the server name, and tell your software not
41+
to verify the certificate. Either way works.

live/main.go

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -180,18 +180,19 @@ func main() {
180180
}
181181
continue
182182

183-
case "exec":
184-
// Execute a query on the database file
185-
err = com.SQLiteExecQueryLive(com.Conf.Live.StorageDir, req.DBOwner, req.DBName, req.RequestingUser, req.Query)
183+
case "execute":
184+
// Execute a SQL statement on the database file
185+
var rowsChanged int
186+
rowsChanged, err = com.SQLiteExecuteQueryLive(com.Conf.Live.StorageDir, req.DBOwner, req.DBName, req.RequestingUser, req.Query)
186187
if err != nil {
187-
err = com.MQExecResponse(msg, ch, com.Conf.Live.Nodename, err.Error())
188+
err = com.MQExecuteResponse(msg, ch, com.Conf.Live.Nodename, 0, err.Error())
188189
continue
189190
}
190191

191-
// Return a success message (empty string in this case) to the caller
192-
err = com.MQExecResponse(msg, ch, com.Conf.Live.Nodename, "")
192+
// Return a success message to the caller
193+
err = com.MQExecuteResponse(msg, ch, com.Conf.Live.Nodename, rowsChanged, "")
193194
if err != nil {
194-
log.Printf("Error: occurred on '%s' in MQExecResponse() while constructing the AMQP execute query response: '%s'", com.Conf.Live.Nodename, err)
195+
log.Printf("Error: occurred on '%s' in MQExecuteResponse() while constructing the AMQP execute query response: '%s'", com.Conf.Live.Nodename, err)
195196
}
196197
continue
197198

0 commit comments

Comments
 (0)