summary | shortlog | log | commit | commitdiff | tree
raw | patch | inline | side by side (parent: 9457a1f)
raw | patch | inline | side by side (parent: 9457a1f)
author | Sebastian Harl <sh@tokkee.org> | |
Tue, 12 May 2015 20:31:44 +0000 (22:31 +0200) | ||
committer | Sebastian Harl <sh@tokkee.org> | |
Tue, 12 May 2015 20:31:44 +0000 (22:31 +0200) |
These are basically taken over from the SysDB webui code which will be based
on this new code in the future.
on this new code in the future.
client/query.go | [new file with mode: 0644] | patch | blob |
client/query_test.go | [new file with mode: 0644] | patch | blob |
diff --git a/client/query.go b/client/query.go
--- /dev/null
+++ b/client/query.go
@@ -0,0 +1,136 @@
+//
+// Copyright (C) 2014-2015 Sebastian 'tokkee' Harl <sh@tokkee.org>
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+// 1. Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// 2. Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
+// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+package client
+
+import (
+ "errors"
+ "fmt"
+ "regexp"
+ "time"
+
+ "github.com/sysdb/go/proto"
+ "github.com/sysdb/go/sysdb"
+)
+
+// An Identifier is a string that may not be quoted or escaped in a query.
+type Identifier string
+
+// The default format for date-time values.
+var dtFormat = "2006-01-02 15:04:05"
+
+func stringify(values ...interface{}) ([]interface{}, error) {
+ str := make([]interface{}, len(values))
+ for i, v := range values {
+ switch val := v.(type) {
+ case uint8, uint16, uint32, uint64, int8, int16, int32, int64, int:
+ str[i] = fmt.Sprintf("%d", val)
+ case float32, float64:
+ str[i] = fmt.Sprintf("%e", val)
+ case Identifier:
+ str[i] = string(val)
+ case string:
+ str[i] = proto.EscapeString(val)
+ case time.Time:
+ str[i] = val.Format(dtFormat)
+ default:
+ return nil, fmt.Errorf("cannot embed value %v of type %T in query", v, v)
+ }
+ }
+ return str, nil
+}
+
+// The fmt package does not expose these errors except through the formatted
+// string. Let's just assume that this pattern never occurs in a real query
+// string (or else, users will have to work around this by not using
+// QueryString()).
+var badArgRE = regexp.MustCompile(`%!.?\(.+`)
+
+// QueryString formats a query string. The query q may include printf string
+// verbs (%s) for each argument. The arguments may be of type Identifier,
+// string, or time.Time and will be formatted to make them suitable for use in
+// a query.
+//
+// This function tries to prevent injection attacks but it's not fool-proof.
+// It will go away once the SysDB network protocol supports arguments to
+// queries.
+func QueryString(q string, args ...interface{}) (string, error) {
+ args, err := stringify(args...)
+ if err != nil {
+ return "", err
+ }
+
+ str := fmt.Sprintf(q, args...)
+
+ // Try to identify format string errors.
+ if e := badArgRE.Find([]byte(str)); e != nil {
+ return "", errors.New(string(e))
+ }
+
+ return str, nil
+}
+
+// Query executes a query on the server. It returns a sysdb object on success.
+func (c *Client) Query(q string) (interface{}, error) {
+ res, err := c.Call(&proto.Message{
+ Type: proto.ConnectionQuery,
+ Raw: []byte(q),
+ })
+ if err != nil {
+ return nil, err
+ }
+ if res.Type != proto.ConnectionData {
+ return nil, fmt.Errorf("unexpected result type %d", res.Type)
+ }
+
+ t, err := res.DataType()
+ if err != nil {
+ return nil, fmt.Errorf("failed to unmarshal response: %v", err)
+ }
+
+ var obj interface{}
+ switch t {
+ case proto.HostList:
+ var hosts []sysdb.Host
+ err = proto.Unmarshal(res, &hosts)
+ obj = hosts
+ case proto.Host:
+ var host sysdb.Host
+ err = proto.Unmarshal(res, &host)
+ obj = &host
+ case proto.Timeseries:
+ var ts sysdb.Timeseries
+ err = proto.Unmarshal(res, &ts)
+ obj = &ts
+ default:
+ return nil, fmt.Errorf("unsupported data type %d", t)
+ }
+ if err != nil {
+ return nil, fmt.Errorf("failed to unmarshal response: %v", err)
+ }
+ return obj, nil
+}
+
+// vim: set tw=78 sw=4 sw=4 noexpandtab :
diff --git a/client/query_test.go b/client/query_test.go
--- /dev/null
+++ b/client/query_test.go
@@ -0,0 +1,66 @@
+//
+// Copyright (C) 2015 Sebastian 'tokkee' Harl <sh@tokkee.org>
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+// 1. Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// 2. Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
+// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+package client
+
+import (
+ "testing"
+ "time"
+)
+
+func TestQueryString(t *testing.T) {
+ ts, _ := time.Parse("2006-01-02 15:04:05", "2006-01-02 15:04:05")
+ for _, test := range []struct {
+ q string
+ args []interface{}
+ want string
+ wantErr bool
+ }{
+ {"some %s; foo %s", []interface{}{"thing", "bar"}, "some 'thing'; foo 'bar'", false},
+ {"s=%s", []interface{}{"'a"}, "s='''a'", false},
+ {"t=%s", []interface{}{ts}, "t=2006-01-02 15:04:05", false},
+ {"i=%s; f=%s", []interface{}{1234, 47.11}, "i=1234; f=4.711000e+01", false},
+ {"t=%d", []interface{}{ts}, "", true},
+ {"some %s; foo %s", []interface{}{"a", "b", "c"}, "", true},
+ {"some %s; foo %s", []interface{}{"a"}, "", true},
+ {"s=%s", []interface{}{`multi
+line
+text`}, "s='multi\nline\ntext'", false},
+ {"s=%d", []interface{}{`multi
+line
+error`}, "", true},
+ } {
+ s, err := QueryString(test.q, test.args...)
+ if s != test.want || (err != nil) != test.wantErr {
+ e := "<nil>"
+ if test.wantErr {
+ e = "<err>"
+ }
+ t.Errorf("QueryString(%q, %v) = %q, %v; want %q, %s", test.q, test.args, s, err, test.want, e)
+ }
+ }
+}
+
+// vim: set tw=78 sw=4 sw=4 noexpandtab :