Skip to content

Commit afda31b

Browse files
committed
Updated
1 parent 17e7edf commit afda31b

File tree

7 files changed

+135
-87
lines changed

7 files changed

+135
-87
lines changed

pkg/sqlite3/results.go

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,10 @@ import (
88

99
// Packages
1010
sqlite3 "github.com/djthorpe/go-sqlite/sys/sqlite3"
11+
1112
// Namespace imports
12-
//. "github.com/djthorpe/go-errors"
13+
. "github.com/djthorpe/go-sqlite"
14+
. "github.com/djthorpe/go-sqlite/pkg/lang"
1315
)
1416

1517
////////////////////////////////////////////////////////////////////////////////
@@ -96,3 +98,26 @@ func (r *Results) RowsAffected() int {
9698
return r.results.RowsAffected()
9799
}
98100
}
101+
102+
// Return the columns for the current results
103+
func (r *Results) Columns() []SQColumn {
104+
if r.results == nil {
105+
return nil
106+
}
107+
cols := make([]SQColumn, r.results.ColumnCount())
108+
for i := range cols {
109+
cols[i] = C(r.results.ColumnName(i)).WithType(r.results.ColumnDeclType(i))
110+
}
111+
return cols
112+
}
113+
114+
func (r *Results) ColumnSource(i int) (string, string, string) {
115+
if r.results == nil {
116+
return "", "", ""
117+
}
118+
schema, table, name := r.results.ColumnDatabaseName(i), r.results.ColumnTableName(i), r.results.ColumnName(i)
119+
if name == "" {
120+
name = r.results.ColumnOriginName(i)
121+
}
122+
return schema, table, name
123+
}

plugin/sqlite3/README.md

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,13 @@ Errors are returned when the status code is not `200 OK`. A typical error respon
7575
}
7676
```
7777

78+
## Plugin Configuration
7879

79-
## Ping Request and Response
80+
TODO
81+
82+
## Requests and Responses
83+
84+
### Ping Request and Response
8085

8186
There are no query arguments for this call. Typically a response will look like this:
8287

@@ -107,11 +112,20 @@ There are no query arguments for this call. Typically a response will look like
107112
}
108113
```
109114

110-
## Schema Request and Response
115+
### Schema Request and Response
111116

112117
There are no query arguments for this call. Typically a response will provide you with information
113118
in the schemas. For example, a typical response may look like this:
114119

115120
TODO
116121

117-
## Table Request and Response
122+
### Table Request and Response
123+
124+
TODO
125+
126+
### Query Request and Response
127+
128+
TODO
129+
130+
### Tokenizer Request and Response
131+

plugin/sqlite3/handlers.go

Lines changed: 46 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -58,11 +58,11 @@ type SchemaTableResponse struct {
5858

5959
type SchemaColumnResponse struct {
6060
Name string `json:"name"`
61-
Table string `json:"table"`
62-
Schema string `json:"schema"`
63-
Type string `json:"type"`
64-
Primary bool `json:"primary"`
65-
Nullable bool `json:"nullable"`
61+
Table string `json:"table,omitempty"`
62+
Schema string `json:"schema,omitempty"`
63+
Type string `json:"type,omitempty"`
64+
Primary bool `json:"primary,omitempty"`
65+
Nullable bool `json:"nullable,omitempty"`
6666
}
6767

6868
type SchemaIndexResponse struct {
@@ -76,12 +76,13 @@ type SqlRequest struct {
7676
}
7777

7878
type SqlResultResponse struct {
79-
Schema string `json:"schema,omitempty"`
80-
Table string `json:"table,omitempty"`
81-
Sql string `json:"sql"`
82-
LastInsertId int64 `json:"last_insert_id,omitempty"`
83-
RowsAffected int `json:"rows_affected,omitempty"`
84-
Result []interface{} `json:"result,omitempty"`
79+
Schema string `json:"schema,omitempty"`
80+
Table string `json:"table,omitempty"`
81+
Sql string `json:"sql"`
82+
LastInsertId int64 `json:"last_insert_id,omitempty"`
83+
RowsAffected int `json:"rows_affected,omitempty"`
84+
Columns []SchemaColumnResponse `json:"columns,omitempty"`
85+
Results []interface{} `json:"results,omitempty"`
8586
}
8687

8788
type TokenizerResponse struct {
@@ -213,16 +214,7 @@ func (p *plugin) ServeSchema(w http.ResponseWriter, req *http.Request) {
213214
})
214215
}
215216
for _, column := range conn.ColumnsForTable(params[0], name) {
216-
col := SchemaColumnResponse{
217-
Name: column.Name(),
218-
Table: name,
219-
Schema: params[0],
220-
Type: column.Type(),
221-
}
222-
if column.Primary() != "" {
223-
col.Primary = true
224-
}
225-
table.Columns = append(table.Columns, col)
217+
table.Columns = append(table.Columns, schemaColumn(params[0], name, column))
226218
}
227219
response.Tables = append(response.Tables, table)
228220
}
@@ -265,6 +257,9 @@ func (p *plugin) ServeTable(w http.ResponseWriter, req *http.Request) {
265257
return
266258
}
267259

260+
// Fix limit to ensure we only steam up to 1K results
261+
q.Limit = uintMin(q.Limit, maxResultLimit)
262+
268263
// Populate response
269264
var response SqlResultResponse
270265
if err := conn.Do(req.Context(), SQLITE_TXN_DEFAULT, func(txn SQTransaction) error {
@@ -372,23 +367,44 @@ func (p *plugin) ServeQuery(w http.ResponseWriter, req *http.Request) {
372367
///////////////////////////////////////////////////////////////////////////////
373368
// PRIVATE METHODS
374369

370+
func schemaColumn(schema, table string, column SQColumn) SchemaColumnResponse {
371+
result := SchemaColumnResponse{
372+
Name: column.Name(),
373+
Table: table,
374+
Schema: schema,
375+
Type: column.Type(),
376+
}
377+
if column.Primary() != "" {
378+
result.Primary = true
379+
}
380+
return result
381+
}
382+
375383
func results(r SQResults) (SqlResultResponse, error) {
376384
result := SqlResultResponse{
377385
Sql: r.ExpandedSQL(),
378386
LastInsertId: r.LastInsertId(),
379387
RowsAffected: r.RowsAffected(),
388+
Columns: []SchemaColumnResponse{},
389+
}
390+
391+
// Set the columns
392+
for i, column := range r.Columns() {
393+
schema, table, _ := r.ColumnSource(i)
394+
result.Columns = append(result.Columns, schemaColumn(schema, table, column))
380395
}
381396

382397
// Iterate through the rows, break when maximum number of results is reached
383398
for {
384-
if row, err := r.Next(); errors.Is(err, io.EOF) {
399+
row, err := r.Next()
400+
if errors.Is(err, io.EOF) {
385401
break
386402
} else if err != nil {
387403
return result, err
388404
} else {
389-
result.Result = append(result.Result, row)
405+
result.Results = append(result.Results, interfaceSliceCopy(row))
390406
}
391-
if len(result.Result) >= maxResultLimit {
407+
if len(result.Results) >= maxResultLimit {
392408
break
393409
}
394410
}
@@ -397,6 +413,12 @@ func results(r SQResults) (SqlResultResponse, error) {
397413
return result, nil
398414
}
399415

416+
func interfaceSliceCopy(v []interface{}) []interface{} {
417+
result := make([]interface{}, len(v))
418+
copy(result, v)
419+
return result
420+
}
421+
400422
func stringSliceContainsElement(v []string, elem string) bool {
401423
for _, v := range v {
402424
if v == elem {

plugin/sqlite3/util.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package main
2+
3+
// uintMin returns the minimum of two uints
4+
func uintMin(a, b uint) uint {
5+
if a < b {
6+
return a
7+
}
8+
return b
9+
}

sqlite.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,8 @@ type SQTransaction interface {
9696
type SQResults interface {
9797
// Return next row, returns nil when all rows consumed
9898
// if types are provided, then returned row is cast to
99-
// appopriate types
99+
// appopriate types. The returned row needs to be copied
100+
// if not transient
100101
Next(...reflect.Type) ([]interface{}, error)
101102

102103
// Return next map of values, or nil if no more rows
@@ -113,6 +114,12 @@ type SQResults interface {
113114

114115
// Return number of changes made of last statement
115116
RowsAffected() int
117+
118+
// Columns returns the column definitions
119+
Columns() []SQColumn
120+
121+
// ColumnTable returns the schema, table and column name for a column index
122+
ColumnSource(int) (string, string, string)
116123
}
117124

118125
// SQAuth is an interface for authenticating an action

sys/sqlite3/connex.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -369,9 +369,10 @@ func (c *ConnEx) ExecEx(q string, fn ExecFunc, v ...interface{}) error {
369369

370370
// Cast row values to string
371371
t := make([]reflect.Type, r.ColumnCount())
372-
n := r.ColumnNames()
372+
n := make([]string, r.ColumnCount())
373373
v := make([]string, r.ColumnCount())
374374
for i := range t {
375+
n[i] = r.ColumnName(i)
375376
t[i] = typeText
376377
}
377378

sys/sqlite3/results.go

Lines changed: 27 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -130,87 +130,57 @@ func (r *Results) ExpandedSQL() string {
130130
}
131131
}
132132

133-
// Return column names for the next row to be fetched
134-
func (r *Results) ColumnNames() []string {
135-
if r.st == nil {
136-
return nil
137-
}
138-
len := r.st.ColumnCount()
139-
result := make([]string, len)
140-
for i := 0; i < len; i++ {
141-
result[i] = r.st.ColumnName(i)
142-
}
143-
return result
144-
}
145-
146133
// Return column count
147134
func (r *Results) ColumnCount() int {
148135
return r.st.ColumnCount()
149136
}
150137

151-
// Return column types for the next row to be fetched
152-
func (r *Results) ColumnTypes() []Type {
138+
// Return column name
139+
func (r *Results) ColumnName(i int) string {
153140
if r.st == nil {
154-
return nil
155-
}
156-
len := r.st.ColumnCount()
157-
result := make([]Type, len)
158-
for i := 0; i < len; i++ {
159-
result[i] = r.st.ColumnType(i)
141+
return ""
160142
}
161-
return result
143+
return r.st.ColumnName(i)
162144
}
163145

164-
// Return column decltypes for the next row to be fetched
165-
func (r *Results) ColumnDeclTypes() []string {
146+
// Return column type
147+
func (r *Results) ColumnType(i int) Type {
166148
if r.st == nil {
167-
return nil
149+
return SQLITE_NULL
168150
}
169-
len := r.st.ColumnCount()
170-
result := make([]string, len)
171-
for i := 0; i < len; i++ {
172-
result[i] = r.st.ColumnDeclType(i)
173-
}
174-
return result
151+
return r.st.ColumnType(i)
175152
}
176153

177-
// Return the source database schema name for the next row to be fetched
178-
func (r *Results) ColumnDatabaseNames() []string {
154+
// Return column decltype
155+
func (r *Results) ColumnDeclType(i int) string {
179156
if r.st == nil {
180-
return nil
181-
}
182-
len := r.st.ColumnCount()
183-
result := make([]string, len)
184-
for i := 0; i < len; i++ {
185-
result[i] = r.st.ColumnDatabaseName(i)
157+
return ""
186158
}
187-
return result
159+
return r.st.ColumnDeclType(i)
188160
}
189161

190-
// Return the source table name for the next row to be fetched
191-
func (r *Results) ColumnTableNames() []string {
162+
// Return the source database schema name
163+
func (r *Results) ColumnDatabaseName(i int) string {
192164
if r.st == nil {
193-
return nil
194-
}
195-
len := r.st.ColumnCount()
196-
result := make([]string, len)
197-
for i := 0; i < len; i++ {
198-
result[i] = r.st.ColumnTableName(i)
165+
return ""
199166
}
200-
return result
167+
return r.st.ColumnDatabaseName(i)
201168
}
202169

203-
// Return the origin for the next row to be fetched
204-
func (r *Results) ColumnOriginNames() []string {
170+
// Return the source table name
171+
func (r *Results) ColumnTableName(i int) string {
205172
if r.st == nil {
206-
return nil
173+
return ""
207174
}
208-
len := r.st.ColumnCount()
209-
result := make([]string, len)
210-
for i := 0; i < len; i++ {
211-
result[i] = r.st.ColumnOriginName(i)
175+
return r.st.ColumnTableName(i)
176+
}
177+
178+
// Return the origin
179+
func (r *Results) ColumnOriginName(i int) string {
180+
if r.st == nil {
181+
return ""
212182
}
213-
return result
183+
return r.st.ColumnOriginName(i)
214184
}
215185

216186
///////////////////////////////////////////////////////////////////////////////

0 commit comments

Comments
 (0)