vendor github.com/openark/golib
This commit is contained in:
parent
f29e63bc71
commit
f2c203382b
201
vendor/github.com/openark/golib/LICENSE
generated
vendored
Normal file
201
vendor/github.com/openark/golib/LICENSE
generated
vendored
Normal file
@ -0,0 +1,201 @@
|
|||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright 2014 Outbrain Inc
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
9
vendor/github.com/openark/golib/README.md
generated
vendored
Normal file
9
vendor/github.com/openark/golib/README.md
generated
vendored
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
Common Go libraries
|
||||||
|
|
||||||
|
To import & use:
|
||||||
|
```
|
||||||
|
go get "github.com/openark/golib/math"
|
||||||
|
go get "github.com/openark/golib/sqlutils"
|
||||||
|
go get "github.com/openark/golib/tests"
|
||||||
|
...
|
||||||
|
```
|
3
vendor/github.com/openark/golib/go.mod
generated
vendored
Normal file
3
vendor/github.com/openark/golib/go.mod
generated
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
module github.com/openark/golib
|
||||||
|
|
||||||
|
go 1.16
|
0
vendor/github.com/openark/golib/go.sum
generated
vendored
Normal file
0
vendor/github.com/openark/golib/go.sum
generated
vendored
Normal file
268
vendor/github.com/openark/golib/log/log.go
generated
vendored
Normal file
268
vendor/github.com/openark/golib/log/log.go
generated
vendored
Normal file
@ -0,0 +1,268 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2014 Outbrain Inc.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package log
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"log/syslog"
|
||||||
|
"os"
|
||||||
|
"runtime/debug"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// LogLevel indicates the severity of a log entry
|
||||||
|
type LogLevel int
|
||||||
|
|
||||||
|
func (this LogLevel) String() string {
|
||||||
|
switch this {
|
||||||
|
case FATAL:
|
||||||
|
return "FATAL"
|
||||||
|
case CRITICAL:
|
||||||
|
return "CRITICAL"
|
||||||
|
case ERROR:
|
||||||
|
return "ERROR"
|
||||||
|
case WARNING:
|
||||||
|
return "WARNING"
|
||||||
|
case NOTICE:
|
||||||
|
return "NOTICE"
|
||||||
|
case INFO:
|
||||||
|
return "INFO"
|
||||||
|
case DEBUG:
|
||||||
|
return "DEBUG"
|
||||||
|
}
|
||||||
|
return "unknown"
|
||||||
|
}
|
||||||
|
|
||||||
|
func LogLevelFromString(logLevelName string) (LogLevel, error) {
|
||||||
|
switch logLevelName {
|
||||||
|
case "FATAL":
|
||||||
|
return FATAL, nil
|
||||||
|
case "CRITICAL":
|
||||||
|
return CRITICAL, nil
|
||||||
|
case "ERROR":
|
||||||
|
return ERROR, nil
|
||||||
|
case "WARNING":
|
||||||
|
return WARNING, nil
|
||||||
|
case "NOTICE":
|
||||||
|
return NOTICE, nil
|
||||||
|
case "INFO":
|
||||||
|
return INFO, nil
|
||||||
|
case "DEBUG":
|
||||||
|
return DEBUG, nil
|
||||||
|
}
|
||||||
|
return 0, fmt.Errorf("Unknown LogLevel name: %+v", logLevelName)
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
FATAL LogLevel = iota
|
||||||
|
CRITICAL
|
||||||
|
ERROR
|
||||||
|
WARNING
|
||||||
|
NOTICE
|
||||||
|
INFO
|
||||||
|
DEBUG
|
||||||
|
)
|
||||||
|
|
||||||
|
const TimeFormat = "2006-01-02 15:04:05"
|
||||||
|
|
||||||
|
// globalLogLevel indicates the global level filter for all logs (only entries with level equals or higher
|
||||||
|
// than this value will be logged)
|
||||||
|
var globalLogLevel LogLevel = DEBUG
|
||||||
|
var printStackTrace bool = false
|
||||||
|
|
||||||
|
// syslogWriter is optional, and defaults to nil (disabled)
|
||||||
|
var syslogLevel LogLevel = ERROR
|
||||||
|
var syslogWriter *syslog.Writer
|
||||||
|
|
||||||
|
// SetPrintStackTrace enables/disables dumping the stack upon error logging
|
||||||
|
func SetPrintStackTrace(shouldPrintStackTrace bool) {
|
||||||
|
printStackTrace = shouldPrintStackTrace
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetLevel sets the global log level. Only entries with level equals or higher than
|
||||||
|
// this value will be logged
|
||||||
|
func SetLevel(logLevel LogLevel) {
|
||||||
|
globalLogLevel = logLevel
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetLevel returns current global log level
|
||||||
|
func GetLevel() LogLevel {
|
||||||
|
return globalLogLevel
|
||||||
|
}
|
||||||
|
|
||||||
|
// EnableSyslogWriter enables, if possible, writes to syslog. These will execute _in addition_ to normal logging
|
||||||
|
func EnableSyslogWriter(tag string) (err error) {
|
||||||
|
syslogWriter, err = syslog.New(syslog.LOG_ERR, tag)
|
||||||
|
if err != nil {
|
||||||
|
syslogWriter = nil
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetSyslogLevel sets the minimal syslog level. Only entries with level equals or higher than
|
||||||
|
// this value will be logged. However, this is also capped by the global log level. That is,
|
||||||
|
// messages with lower level than global-log-level will be discarded at any case.
|
||||||
|
func SetSyslogLevel(logLevel LogLevel) {
|
||||||
|
syslogLevel = logLevel
|
||||||
|
}
|
||||||
|
|
||||||
|
// logFormattedEntry nicely formats and emits a log entry
|
||||||
|
func logFormattedEntry(logLevel LogLevel, message string, args ...interface{}) string {
|
||||||
|
if logLevel > globalLogLevel {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
// if TZ env variable is set, update the timestamp timezone
|
||||||
|
localizedTime := time.Now()
|
||||||
|
tzLocation := os.Getenv("TZ")
|
||||||
|
if tzLocation != "" {
|
||||||
|
location, err := time.LoadLocation(tzLocation)
|
||||||
|
if err == nil { // if invalid tz location was provided, just leave it as the default
|
||||||
|
localizedTime = time.Now().In(location)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
msgArgs := fmt.Sprintf(message, args...)
|
||||||
|
entryString := fmt.Sprintf("%s %s %s", localizedTime.Format(TimeFormat), logLevel, msgArgs)
|
||||||
|
fmt.Fprintln(os.Stderr, entryString)
|
||||||
|
|
||||||
|
if syslogWriter != nil {
|
||||||
|
go func() error {
|
||||||
|
if logLevel > syslogLevel {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
switch logLevel {
|
||||||
|
case FATAL:
|
||||||
|
return syslogWriter.Emerg(msgArgs)
|
||||||
|
case CRITICAL:
|
||||||
|
return syslogWriter.Crit(msgArgs)
|
||||||
|
case ERROR:
|
||||||
|
return syslogWriter.Err(msgArgs)
|
||||||
|
case WARNING:
|
||||||
|
return syslogWriter.Warning(msgArgs)
|
||||||
|
case NOTICE:
|
||||||
|
return syslogWriter.Notice(msgArgs)
|
||||||
|
case INFO:
|
||||||
|
return syslogWriter.Info(msgArgs)
|
||||||
|
case DEBUG:
|
||||||
|
return syslogWriter.Debug(msgArgs)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
return entryString
|
||||||
|
}
|
||||||
|
|
||||||
|
// logEntry emits a formatted log entry
|
||||||
|
func logEntry(logLevel LogLevel, message string, args ...interface{}) string {
|
||||||
|
entryString := message
|
||||||
|
for _, s := range args {
|
||||||
|
entryString += fmt.Sprintf(" %s", s)
|
||||||
|
}
|
||||||
|
return logFormattedEntry(logLevel, entryString)
|
||||||
|
}
|
||||||
|
|
||||||
|
// logErrorEntry emits a log entry based on given error object
|
||||||
|
func logErrorEntry(logLevel LogLevel, err error) error {
|
||||||
|
if err == nil {
|
||||||
|
// No error
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
entryString := fmt.Sprintf("%+v", err)
|
||||||
|
logEntry(logLevel, entryString)
|
||||||
|
if printStackTrace {
|
||||||
|
debug.PrintStack()
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func Debug(message string, args ...interface{}) string {
|
||||||
|
return logEntry(DEBUG, message, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Debugf(message string, args ...interface{}) string {
|
||||||
|
return logFormattedEntry(DEBUG, message, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Info(message string, args ...interface{}) string {
|
||||||
|
return logEntry(INFO, message, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Infof(message string, args ...interface{}) string {
|
||||||
|
return logFormattedEntry(INFO, message, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Notice(message string, args ...interface{}) string {
|
||||||
|
return logEntry(NOTICE, message, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Noticef(message string, args ...interface{}) string {
|
||||||
|
return logFormattedEntry(NOTICE, message, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Warning(message string, args ...interface{}) error {
|
||||||
|
return errors.New(logEntry(WARNING, message, args...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func Warningf(message string, args ...interface{}) error {
|
||||||
|
return errors.New(logFormattedEntry(WARNING, message, args...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func Error(message string, args ...interface{}) error {
|
||||||
|
return errors.New(logEntry(ERROR, message, args...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func Errorf(message string, args ...interface{}) error {
|
||||||
|
return errors.New(logFormattedEntry(ERROR, message, args...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func Errore(err error) error {
|
||||||
|
return logErrorEntry(ERROR, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Critical(message string, args ...interface{}) error {
|
||||||
|
return errors.New(logEntry(CRITICAL, message, args...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func Criticalf(message string, args ...interface{}) error {
|
||||||
|
return errors.New(logFormattedEntry(CRITICAL, message, args...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func Criticale(err error) error {
|
||||||
|
return logErrorEntry(CRITICAL, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fatal emits a FATAL level entry and exists the program
|
||||||
|
func Fatal(message string, args ...interface{}) error {
|
||||||
|
logEntry(FATAL, message, args...)
|
||||||
|
os.Exit(1)
|
||||||
|
return errors.New(logEntry(CRITICAL, message, args...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fatalf emits a FATAL level entry and exists the program
|
||||||
|
func Fatalf(message string, args ...interface{}) error {
|
||||||
|
logFormattedEntry(FATAL, message, args...)
|
||||||
|
os.Exit(1)
|
||||||
|
return errors.New(logFormattedEntry(CRITICAL, message, args...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fatale emits a FATAL level entry and exists the program
|
||||||
|
func Fatale(err error) error {
|
||||||
|
logErrorEntry(FATAL, err)
|
||||||
|
os.Exit(1)
|
||||||
|
return err
|
||||||
|
}
|
119
vendor/github.com/openark/golib/math/math.go
generated
vendored
Normal file
119
vendor/github.com/openark/golib/math/math.go
generated
vendored
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2014 Shlomi Noach.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package math
|
||||||
|
|
||||||
|
func MinInt(i1, i2 int) int {
|
||||||
|
if i1 < i2 {
|
||||||
|
return i1
|
||||||
|
}
|
||||||
|
return i2
|
||||||
|
}
|
||||||
|
|
||||||
|
func MaxInt(i1, i2 int) int {
|
||||||
|
if i1 > i2 {
|
||||||
|
return i1
|
||||||
|
}
|
||||||
|
return i2
|
||||||
|
}
|
||||||
|
|
||||||
|
func MinInt64(i1, i2 int64) int64 {
|
||||||
|
if i1 < i2 {
|
||||||
|
return i1
|
||||||
|
}
|
||||||
|
return i2
|
||||||
|
}
|
||||||
|
|
||||||
|
func MaxInt64(i1, i2 int64) int64 {
|
||||||
|
if i1 > i2 {
|
||||||
|
return i1
|
||||||
|
}
|
||||||
|
return i2
|
||||||
|
}
|
||||||
|
|
||||||
|
func MinUInt(i1, i2 uint) uint {
|
||||||
|
if i1 < i2 {
|
||||||
|
return i1
|
||||||
|
}
|
||||||
|
return i2
|
||||||
|
}
|
||||||
|
|
||||||
|
func MaxUInt(i1, i2 uint) uint {
|
||||||
|
if i1 > i2 {
|
||||||
|
return i1
|
||||||
|
}
|
||||||
|
return i2
|
||||||
|
}
|
||||||
|
|
||||||
|
func MinUInt64(i1, i2 uint64) uint64 {
|
||||||
|
if i1 < i2 {
|
||||||
|
return i1
|
||||||
|
}
|
||||||
|
return i2
|
||||||
|
}
|
||||||
|
|
||||||
|
func MaxUInt64(i1, i2 uint64) uint64 {
|
||||||
|
if i1 > i2 {
|
||||||
|
return i1
|
||||||
|
}
|
||||||
|
return i2
|
||||||
|
}
|
||||||
|
|
||||||
|
func MinString(i1, i2 string) string {
|
||||||
|
if i1 < i2 {
|
||||||
|
return i1
|
||||||
|
}
|
||||||
|
return i2
|
||||||
|
}
|
||||||
|
|
||||||
|
func MaxString(i1, i2 string) string {
|
||||||
|
if i1 > i2 {
|
||||||
|
return i1
|
||||||
|
}
|
||||||
|
return i2
|
||||||
|
}
|
||||||
|
|
||||||
|
// TernaryString acts like a "? :" C-style ternary operator for strings
|
||||||
|
func TernaryString(condition bool, resTrue string, resFalse string) string {
|
||||||
|
if condition {
|
||||||
|
return resTrue
|
||||||
|
}
|
||||||
|
return resFalse
|
||||||
|
}
|
||||||
|
|
||||||
|
// TernaryString acts like a "? :" C-style ternary operator for ints
|
||||||
|
func TernaryInt(condition bool, resTrue int, resFalse int) int {
|
||||||
|
if condition {
|
||||||
|
return resTrue
|
||||||
|
}
|
||||||
|
return resFalse
|
||||||
|
}
|
||||||
|
|
||||||
|
// AbsInt is an ABS function for int type
|
||||||
|
func AbsInt(i int) int {
|
||||||
|
if i >= 0 {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
return -i
|
||||||
|
}
|
||||||
|
|
||||||
|
// AbsInt64 is an ABS function for int64 type
|
||||||
|
func AbsInt64(i int64) int64 {
|
||||||
|
if i >= 0 {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
return -i
|
||||||
|
}
|
49
vendor/github.com/openark/golib/sqlutils/dialect.go
generated
vendored
Normal file
49
vendor/github.com/openark/golib/sqlutils/dialect.go
generated
vendored
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2017 GitHub Inc.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package sqlutils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type regexpMap struct {
|
||||||
|
r *regexp.Regexp
|
||||||
|
replacement string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *regexpMap) process(text string) (result string) {
|
||||||
|
return this.r.ReplaceAllString(text, this.replacement)
|
||||||
|
}
|
||||||
|
|
||||||
|
func rmap(regexpExpression string, replacement string) regexpMap {
|
||||||
|
return regexpMap{
|
||||||
|
r: regexp.MustCompile(regexpSpaces(regexpExpression)),
|
||||||
|
replacement: replacement,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func regexpSpaces(statement string) string {
|
||||||
|
return strings.Replace(statement, " ", `[\s]+`, -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func applyConversions(statement string, conversions []regexpMap) string {
|
||||||
|
for _, rmap := range conversions {
|
||||||
|
statement = rmap.process(statement)
|
||||||
|
}
|
||||||
|
return statement
|
||||||
|
}
|
130
vendor/github.com/openark/golib/sqlutils/sqlite_dialect.go
generated
vendored
Normal file
130
vendor/github.com/openark/golib/sqlutils/sqlite_dialect.go
generated
vendored
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2017 GitHub Inc.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// What's this about?
|
||||||
|
// This is a brute-force regular-expression based conversion from MySQL syntax to sqlite3 syntax.
|
||||||
|
// It is NOT meant to be a general purpose solution and is only expected & confirmed to run on
|
||||||
|
// queries issued by orchestrator. There are known limitations to this design.
|
||||||
|
// It's not even pretty.
|
||||||
|
// In fact...
|
||||||
|
// Well, it gets the job done at this time. Call it debt.
|
||||||
|
|
||||||
|
package sqlutils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"regexp"
|
||||||
|
)
|
||||||
|
|
||||||
|
var sqlite3CreateTableConversions = []regexpMap{
|
||||||
|
rmap(`(?i) (character set|charset) [\S]+`, ``),
|
||||||
|
rmap(`(?i)int unsigned`, `int`),
|
||||||
|
rmap(`(?i)int[\s]*[(][\s]*([0-9]+)[\s]*[)] unsigned`, `int`),
|
||||||
|
rmap(`(?i)engine[\s]*=[\s]*(innodb|myisam|ndb|memory|tokudb)`, ``),
|
||||||
|
rmap(`(?i)DEFAULT CHARSET[\s]*=[\s]*[\S]+`, ``),
|
||||||
|
rmap(`(?i)[\S]*int( not null|) auto_increment`, `integer`),
|
||||||
|
rmap(`(?i)comment '[^']*'`, ``),
|
||||||
|
rmap(`(?i)after [\S]+`, ``),
|
||||||
|
rmap(`(?i)alter table ([\S]+) add (index|key) ([\S]+) (.+)`, `create index ${3}_${1} on $1 $4`),
|
||||||
|
rmap(`(?i)alter table ([\S]+) add unique (index|key) ([\S]+) (.+)`, `create unique index ${3}_${1} on $1 $4`),
|
||||||
|
rmap(`(?i)([\S]+) enum[\s]*([(].*?[)])`, `$1 text check($1 in $2)`),
|
||||||
|
rmap(`(?i)([\s\S]+[/][*] sqlite3-skip [*][/][\s\S]+)`, ``),
|
||||||
|
rmap(`(?i)timestamp default current_timestamp`, `timestamp default ('')`),
|
||||||
|
rmap(`(?i)timestamp not null default current_timestamp`, `timestamp not null default ('')`),
|
||||||
|
|
||||||
|
rmap(`(?i)add column (.*int) not null[\s]*$`, `add column $1 not null default 0`),
|
||||||
|
rmap(`(?i)add column (.* text) not null[\s]*$`, `add column $1 not null default ''`),
|
||||||
|
rmap(`(?i)add column (.* varchar.*) not null[\s]*$`, `add column $1 not null default ''`),
|
||||||
|
}
|
||||||
|
|
||||||
|
var sqlite3InsertConversions = []regexpMap{
|
||||||
|
rmap(`(?i)insert ignore ([\s\S]+) on duplicate key update [\s\S]+`, `insert or ignore $1`),
|
||||||
|
rmap(`(?i)insert ignore`, `insert or ignore`),
|
||||||
|
rmap(`(?i)now[(][)]`, `datetime('now')`),
|
||||||
|
rmap(`(?i)insert into ([\s\S]+) on duplicate key update [\s\S]+`, `replace into $1`),
|
||||||
|
}
|
||||||
|
|
||||||
|
var sqlite3GeneralConversions = []regexpMap{
|
||||||
|
rmap(`(?i)now[(][)][\s]*[-][\s]*interval [?] ([\w]+)`, `datetime('now', printf('-%d $1', ?))`),
|
||||||
|
rmap(`(?i)now[(][)][\s]*[+][\s]*interval [?] ([\w]+)`, `datetime('now', printf('+%d $1', ?))`),
|
||||||
|
rmap(`(?i)now[(][)][\s]*[-][\s]*interval ([0-9.]+) ([\w]+)`, `datetime('now', '-${1} $2')`),
|
||||||
|
rmap(`(?i)now[(][)][\s]*[+][\s]*interval ([0-9.]+) ([\w]+)`, `datetime('now', '+${1} $2')`),
|
||||||
|
|
||||||
|
rmap(`(?i)[=<>\s]([\S]+[.][\S]+)[\s]*[-][\s]*interval [?] ([\w]+)`, ` datetime($1, printf('-%d $2', ?))`),
|
||||||
|
rmap(`(?i)[=<>\s]([\S]+[.][\S]+)[\s]*[+][\s]*interval [?] ([\w]+)`, ` datetime($1, printf('+%d $2', ?))`),
|
||||||
|
|
||||||
|
rmap(`(?i)unix_timestamp[(][)]`, `strftime('%s', 'now')`),
|
||||||
|
rmap(`(?i)unix_timestamp[(]([^)]+)[)]`, `strftime('%s', $1)`),
|
||||||
|
rmap(`(?i)now[(][)]`, `datetime('now')`),
|
||||||
|
rmap(`(?i)cast[(][\s]*([\S]+) as signed[\s]*[)]`, `cast($1 as integer)`),
|
||||||
|
|
||||||
|
rmap(`(?i)\bconcat[(][\s]*([^,)]+)[\s]*,[\s]*([^,)]+)[\s]*[)]`, `($1 || $2)`),
|
||||||
|
rmap(`(?i)\bconcat[(][\s]*([^,)]+)[\s]*,[\s]*([^,)]+)[\s]*,[\s]*([^,)]+)[\s]*[)]`, `($1 || $2 || $3)`),
|
||||||
|
|
||||||
|
rmap(`(?i) rlike `, ` like `),
|
||||||
|
|
||||||
|
rmap(`(?i)create index([\s\S]+)[(][\s]*[0-9]+[\s]*[)]([\s\S]+)`, `create index ${1}${2}`),
|
||||||
|
rmap(`(?i)drop index ([\S]+) on ([\S]+)`, `drop index if exists $1`),
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
sqlite3IdentifyCreateTableStatement = regexp.MustCompile(regexpSpaces(`(?i)^[\s]*create table`))
|
||||||
|
sqlite3IdentifyCreateIndexStatement = regexp.MustCompile(regexpSpaces(`(?i)^[\s]*create( unique|) index`))
|
||||||
|
sqlite3IdentifyDropIndexStatement = regexp.MustCompile(regexpSpaces(`(?i)^[\s]*drop index`))
|
||||||
|
sqlite3IdentifyAlterTableStatement = regexp.MustCompile(regexpSpaces(`(?i)^[\s]*alter table`))
|
||||||
|
sqlite3IdentifyInsertStatement = regexp.MustCompile(regexpSpaces(`(?i)^[\s]*(insert|replace)`))
|
||||||
|
)
|
||||||
|
|
||||||
|
func IsInsert(statement string) bool {
|
||||||
|
return sqlite3IdentifyInsertStatement.MatchString(statement)
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsCreateTable(statement string) bool {
|
||||||
|
return sqlite3IdentifyCreateTableStatement.MatchString(statement)
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsCreateIndex(statement string) bool {
|
||||||
|
return sqlite3IdentifyCreateIndexStatement.MatchString(statement)
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsDropIndex(statement string) bool {
|
||||||
|
return sqlite3IdentifyDropIndexStatement.MatchString(statement)
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsAlterTable(statement string) bool {
|
||||||
|
return sqlite3IdentifyAlterTableStatement.MatchString(statement)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ToSqlite3CreateTable(statement string) string {
|
||||||
|
return applyConversions(statement, sqlite3CreateTableConversions)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ToSqlite3Insert(statement string) string {
|
||||||
|
return applyConversions(statement, sqlite3InsertConversions)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ToSqlite3Dialect(statement string) (translated string) {
|
||||||
|
if IsCreateTable(statement) {
|
||||||
|
return ToSqlite3CreateTable(statement)
|
||||||
|
}
|
||||||
|
if IsAlterTable(statement) {
|
||||||
|
return ToSqlite3CreateTable(statement)
|
||||||
|
}
|
||||||
|
statement = applyConversions(statement, sqlite3GeneralConversions)
|
||||||
|
if IsInsert(statement) {
|
||||||
|
return ToSqlite3Insert(statement)
|
||||||
|
}
|
||||||
|
return statement
|
||||||
|
}
|
242
vendor/github.com/openark/golib/sqlutils/sqlite_dialect_test.go
generated
vendored
Normal file
242
vendor/github.com/openark/golib/sqlutils/sqlite_dialect_test.go
generated
vendored
Normal file
@ -0,0 +1,242 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2017 GitHub Inc.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package sqlutils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
test "github.com/openark/golib/tests"
|
||||||
|
)
|
||||||
|
|
||||||
|
var spacesRegexp = regexp.MustCompile(`[\s]+`)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
}
|
||||||
|
|
||||||
|
func stripSpaces(statement string) string {
|
||||||
|
statement = strings.TrimSpace(statement)
|
||||||
|
statement = spacesRegexp.ReplaceAllString(statement, " ")
|
||||||
|
return statement
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIsCreateTable(t *testing.T) {
|
||||||
|
test.S(t).ExpectTrue(IsCreateTable("create table t(id int)"))
|
||||||
|
test.S(t).ExpectTrue(IsCreateTable(" create table t(id int)"))
|
||||||
|
test.S(t).ExpectTrue(IsCreateTable("CREATE TABLE t(id int)"))
|
||||||
|
test.S(t).ExpectTrue(IsCreateTable(`
|
||||||
|
create table t(id int)
|
||||||
|
`))
|
||||||
|
test.S(t).ExpectFalse(IsCreateTable("where create table t(id int)"))
|
||||||
|
test.S(t).ExpectFalse(IsCreateTable("insert"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestToSqlite3CreateTable(t *testing.T) {
|
||||||
|
{
|
||||||
|
statement := "create table t(id int)"
|
||||||
|
result := ToSqlite3CreateTable(statement)
|
||||||
|
test.S(t).ExpectEquals(result, statement)
|
||||||
|
}
|
||||||
|
{
|
||||||
|
statement := "create table t(id int, v varchar(123) CHARACTER SET ascii NOT NULL default '')"
|
||||||
|
result := ToSqlite3CreateTable(statement)
|
||||||
|
test.S(t).ExpectEquals(result, "create table t(id int, v varchar(123) NOT NULL default '')")
|
||||||
|
}
|
||||||
|
{
|
||||||
|
statement := "create table t(id int, v varchar ( 123 ) CHARACTER SET ascii NOT NULL default '')"
|
||||||
|
result := ToSqlite3CreateTable(statement)
|
||||||
|
test.S(t).ExpectEquals(result, "create table t(id int, v varchar ( 123 ) NOT NULL default '')")
|
||||||
|
}
|
||||||
|
{
|
||||||
|
statement := "create table t(i smallint unsigned)"
|
||||||
|
result := ToSqlite3CreateTable(statement)
|
||||||
|
test.S(t).ExpectEquals(result, "create table t(i smallint)")
|
||||||
|
}
|
||||||
|
{
|
||||||
|
statement := "create table t(i smallint(5) unsigned)"
|
||||||
|
result := ToSqlite3CreateTable(statement)
|
||||||
|
test.S(t).ExpectEquals(result, "create table t(i smallint)")
|
||||||
|
}
|
||||||
|
{
|
||||||
|
statement := "create table t(i smallint ( 5 ) unsigned)"
|
||||||
|
result := ToSqlite3CreateTable(statement)
|
||||||
|
test.S(t).ExpectEquals(result, "create table t(i smallint)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestToSqlite3AlterTable(t *testing.T) {
|
||||||
|
{
|
||||||
|
statement := `
|
||||||
|
ALTER TABLE
|
||||||
|
database_instance
|
||||||
|
ADD COLUMN sql_delay INT UNSIGNED NOT NULL AFTER slave_lag_seconds
|
||||||
|
`
|
||||||
|
result := stripSpaces(ToSqlite3Dialect(statement))
|
||||||
|
test.S(t).ExpectEquals(result, stripSpaces(`
|
||||||
|
ALTER TABLE
|
||||||
|
database_instance
|
||||||
|
add column sql_delay int not null default 0
|
||||||
|
`))
|
||||||
|
}
|
||||||
|
{
|
||||||
|
statement := `
|
||||||
|
ALTER TABLE
|
||||||
|
database_instance
|
||||||
|
ADD INDEX master_host_port_idx (master_host, master_port)
|
||||||
|
`
|
||||||
|
result := stripSpaces(ToSqlite3Dialect(statement))
|
||||||
|
test.S(t).ExpectEquals(result, stripSpaces(`
|
||||||
|
create index
|
||||||
|
master_host_port_idx_database_instance
|
||||||
|
on database_instance (master_host, master_port)
|
||||||
|
`))
|
||||||
|
}
|
||||||
|
{
|
||||||
|
statement := `
|
||||||
|
ALTER TABLE
|
||||||
|
topology_recovery
|
||||||
|
ADD KEY last_detection_idx (last_detection_id)
|
||||||
|
`
|
||||||
|
result := stripSpaces(ToSqlite3Dialect(statement))
|
||||||
|
test.S(t).ExpectEquals(result, stripSpaces(`
|
||||||
|
create index
|
||||||
|
last_detection_idx_topology_recovery
|
||||||
|
on topology_recovery (last_detection_id)
|
||||||
|
`))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCreateIndex(t *testing.T) {
|
||||||
|
{
|
||||||
|
statement := `
|
||||||
|
create index
|
||||||
|
master_host_port_idx_database_instance
|
||||||
|
on database_instance (master_host(128), master_port)
|
||||||
|
`
|
||||||
|
result := stripSpaces(ToSqlite3Dialect(statement))
|
||||||
|
test.S(t).ExpectEquals(result, stripSpaces(`
|
||||||
|
create index
|
||||||
|
master_host_port_idx_database_instance
|
||||||
|
on database_instance (master_host, master_port)
|
||||||
|
`))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIsInsert(t *testing.T) {
|
||||||
|
test.S(t).ExpectTrue(IsInsert("insert into t"))
|
||||||
|
test.S(t).ExpectTrue(IsInsert("insert ignore into t"))
|
||||||
|
test.S(t).ExpectTrue(IsInsert(`
|
||||||
|
insert ignore into t
|
||||||
|
`))
|
||||||
|
test.S(t).ExpectFalse(IsInsert("where create table t(id int)"))
|
||||||
|
test.S(t).ExpectFalse(IsInsert("create table t(id int)"))
|
||||||
|
test.S(t).ExpectTrue(IsInsert(`
|
||||||
|
insert into
|
||||||
|
cluster_domain_name (cluster_name, domain_name, last_registered)
|
||||||
|
values
|
||||||
|
(?, ?, datetime('now'))
|
||||||
|
on duplicate key update
|
||||||
|
domain_name=values(domain_name),
|
||||||
|
last_registered=values(last_registered)
|
||||||
|
`))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestToSqlite3Insert(t *testing.T) {
|
||||||
|
{
|
||||||
|
statement := `
|
||||||
|
insert into
|
||||||
|
cluster_domain_name (cluster_name, domain_name, last_registered)
|
||||||
|
values
|
||||||
|
(?, ?, datetime('now'))
|
||||||
|
on duplicate key update
|
||||||
|
domain_name=values(domain_name),
|
||||||
|
last_registered=values(last_registered)
|
||||||
|
`
|
||||||
|
result := stripSpaces(ToSqlite3Dialect(statement))
|
||||||
|
test.S(t).ExpectEquals(result, stripSpaces(`
|
||||||
|
replace into
|
||||||
|
cluster_domain_name (cluster_name, domain_name, last_registered)
|
||||||
|
values
|
||||||
|
(?, ?, datetime('now'))
|
||||||
|
`))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestToSqlite3GeneralConversions(t *testing.T) {
|
||||||
|
{
|
||||||
|
statement := "select now()"
|
||||||
|
result := ToSqlite3Dialect(statement)
|
||||||
|
test.S(t).ExpectEquals(result, "select datetime('now')")
|
||||||
|
}
|
||||||
|
{
|
||||||
|
statement := "select now() - interval ? second"
|
||||||
|
result := ToSqlite3Dialect(statement)
|
||||||
|
test.S(t).ExpectEquals(result, "select datetime('now', printf('-%d second', ?))")
|
||||||
|
}
|
||||||
|
{
|
||||||
|
statement := "select now() + interval ? minute"
|
||||||
|
result := ToSqlite3Dialect(statement)
|
||||||
|
test.S(t).ExpectEquals(result, "select datetime('now', printf('+%d minute', ?))")
|
||||||
|
}
|
||||||
|
{
|
||||||
|
statement := "select now() + interval 5 minute"
|
||||||
|
result := ToSqlite3Dialect(statement)
|
||||||
|
test.S(t).ExpectEquals(result, "select datetime('now', '+5 minute')")
|
||||||
|
}
|
||||||
|
{
|
||||||
|
statement := "select some_table.some_column + interval ? minute"
|
||||||
|
result := ToSqlite3Dialect(statement)
|
||||||
|
test.S(t).ExpectEquals(result, "select datetime(some_table.some_column, printf('+%d minute', ?))")
|
||||||
|
}
|
||||||
|
{
|
||||||
|
statement := "AND master_instance.last_attempted_check <= master_instance.last_seen + interval ? minute"
|
||||||
|
result := ToSqlite3Dialect(statement)
|
||||||
|
test.S(t).ExpectEquals(result, "AND master_instance.last_attempted_check <= datetime(master_instance.last_seen, printf('+%d minute', ?))")
|
||||||
|
}
|
||||||
|
{
|
||||||
|
statement := "select concat(master_instance.port, '') as port"
|
||||||
|
result := ToSqlite3Dialect(statement)
|
||||||
|
test.S(t).ExpectEquals(result, "select (master_instance.port || '') as port")
|
||||||
|
}
|
||||||
|
{
|
||||||
|
statement := "select concat( 'abc' , 'def') as s"
|
||||||
|
result := ToSqlite3Dialect(statement)
|
||||||
|
test.S(t).ExpectEquals(result, "select ('abc' || 'def') as s")
|
||||||
|
}
|
||||||
|
{
|
||||||
|
statement := "select concat( 'abc' , 'def', last.col) as s"
|
||||||
|
result := ToSqlite3Dialect(statement)
|
||||||
|
test.S(t).ExpectEquals(result, "select ('abc' || 'def' || last.col) as s")
|
||||||
|
}
|
||||||
|
{
|
||||||
|
statement := "select concat(myself.only) as s"
|
||||||
|
result := ToSqlite3Dialect(statement)
|
||||||
|
test.S(t).ExpectEquals(result, "select concat(myself.only) as s")
|
||||||
|
}
|
||||||
|
{
|
||||||
|
statement := "select concat(1, '2', 3, '4') as s"
|
||||||
|
result := ToSqlite3Dialect(statement)
|
||||||
|
test.S(t).ExpectEquals(result, "select concat(1, '2', 3, '4') as s")
|
||||||
|
}
|
||||||
|
{
|
||||||
|
statement := "select group_concat( 'abc' , 'def') as s"
|
||||||
|
result := ToSqlite3Dialect(statement)
|
||||||
|
test.S(t).ExpectEquals(result, "select group_concat( 'abc' , 'def') as s")
|
||||||
|
}
|
||||||
|
}
|
427
vendor/github.com/openark/golib/sqlutils/sqlutils.go
generated
vendored
Normal file
427
vendor/github.com/openark/golib/sqlutils/sqlutils.go
generated
vendored
Normal file
@ -0,0 +1,427 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2014 Outbrain Inc.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package sqlutils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/openark/golib/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
const DateTimeFormat = "2006-01-02 15:04:05.999999"
|
||||||
|
|
||||||
|
// RowMap represents one row in a result set. Its objective is to allow
|
||||||
|
// for easy, typed getters by column name.
|
||||||
|
type RowMap map[string]CellData
|
||||||
|
|
||||||
|
// Cell data is the result of a single (atomic) column in a single row
|
||||||
|
type CellData sql.NullString
|
||||||
|
|
||||||
|
func (this *CellData) MarshalJSON() ([]byte, error) {
|
||||||
|
if this.Valid {
|
||||||
|
return json.Marshal(this.String)
|
||||||
|
} else {
|
||||||
|
return json.Marshal(nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON reds this object from JSON
|
||||||
|
func (this *CellData) UnmarshalJSON(b []byte) error {
|
||||||
|
var s string
|
||||||
|
if err := json.Unmarshal(b, &s); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
(*this).String = s
|
||||||
|
(*this).Valid = true
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *CellData) NullString() *sql.NullString {
|
||||||
|
return (*sql.NullString)(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RowData is the result of a single row, in positioned array format
|
||||||
|
type RowData []CellData
|
||||||
|
|
||||||
|
// MarshalJSON will marshal this map as JSON
|
||||||
|
func (this *RowData) MarshalJSON() ([]byte, error) {
|
||||||
|
cells := make([](*CellData), len(*this), len(*this))
|
||||||
|
for i, val := range *this {
|
||||||
|
d := CellData(val)
|
||||||
|
cells[i] = &d
|
||||||
|
}
|
||||||
|
return json.Marshal(cells)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *RowData) Args() []interface{} {
|
||||||
|
result := make([]interface{}, len(*this))
|
||||||
|
for i := range *this {
|
||||||
|
result[i] = (*(*this)[i].NullString())
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResultData is an ordered row set of RowData
|
||||||
|
type ResultData []RowData
|
||||||
|
type NamedResultData struct {
|
||||||
|
Columns []string
|
||||||
|
Data ResultData
|
||||||
|
}
|
||||||
|
|
||||||
|
var EmptyResultData = ResultData{}
|
||||||
|
|
||||||
|
func (this *RowMap) GetString(key string) string {
|
||||||
|
return (*this)[key].String
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetStringD returns a string from the map, or a default value if the key does not exist
|
||||||
|
func (this *RowMap) GetStringD(key string, def string) string {
|
||||||
|
if cell, ok := (*this)[key]; ok {
|
||||||
|
return cell.String
|
||||||
|
}
|
||||||
|
return def
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *RowMap) GetInt64(key string) int64 {
|
||||||
|
res, _ := strconv.ParseInt(this.GetString(key), 10, 0)
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *RowMap) GetNullInt64(key string) sql.NullInt64 {
|
||||||
|
i, err := strconv.ParseInt(this.GetString(key), 10, 0)
|
||||||
|
if err == nil {
|
||||||
|
return sql.NullInt64{Int64: i, Valid: true}
|
||||||
|
} else {
|
||||||
|
return sql.NullInt64{Valid: false}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *RowMap) GetInt(key string) int {
|
||||||
|
res, _ := strconv.Atoi(this.GetString(key))
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *RowMap) GetIntD(key string, def int) int {
|
||||||
|
res, err := strconv.Atoi(this.GetString(key))
|
||||||
|
if err != nil {
|
||||||
|
return def
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *RowMap) GetUint(key string) uint {
|
||||||
|
res, _ := strconv.ParseUint(this.GetString(key), 10, 0)
|
||||||
|
return uint(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *RowMap) GetUintD(key string, def uint) uint {
|
||||||
|
res, err := strconv.Atoi(this.GetString(key))
|
||||||
|
if err != nil {
|
||||||
|
return def
|
||||||
|
}
|
||||||
|
return uint(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *RowMap) GetUint64(key string) uint64 {
|
||||||
|
res, _ := strconv.ParseUint(this.GetString(key), 10, 0)
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *RowMap) GetUint64D(key string, def uint64) uint64 {
|
||||||
|
res, err := strconv.ParseUint(this.GetString(key), 10, 0)
|
||||||
|
if err != nil {
|
||||||
|
return def
|
||||||
|
}
|
||||||
|
return uint64(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *RowMap) GetBool(key string) bool {
|
||||||
|
return this.GetInt(key) != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *RowMap) GetTime(key string) time.Time {
|
||||||
|
if t, err := time.Parse(DateTimeFormat, this.GetString(key)); err == nil {
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
return time.Time{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// knownDBs is a DB cache by uri
|
||||||
|
var knownDBs map[string]*sql.DB = make(map[string]*sql.DB)
|
||||||
|
var knownDBsMutex = &sync.Mutex{}
|
||||||
|
|
||||||
|
// GetDB returns a DB instance based on uri.
|
||||||
|
// bool result indicates whether the DB was returned from cache; err
|
||||||
|
func GetGenericDB(driverName, dataSourceName string) (*sql.DB, bool, error) {
|
||||||
|
knownDBsMutex.Lock()
|
||||||
|
defer func() {
|
||||||
|
knownDBsMutex.Unlock()
|
||||||
|
}()
|
||||||
|
|
||||||
|
var exists bool
|
||||||
|
if _, exists = knownDBs[dataSourceName]; !exists {
|
||||||
|
if db, err := sql.Open(driverName, dataSourceName); err == nil {
|
||||||
|
knownDBs[dataSourceName] = db
|
||||||
|
} else {
|
||||||
|
return db, exists, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return knownDBs[dataSourceName], exists, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDB returns a MySQL DB instance based on uri.
|
||||||
|
// bool result indicates whether the DB was returned from cache; err
|
||||||
|
func GetDB(mysql_uri string) (*sql.DB, bool, error) {
|
||||||
|
return GetGenericDB("mysql", mysql_uri)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDB returns a SQLite DB instance based on DB file name.
|
||||||
|
// bool result indicates whether the DB was returned from cache; err
|
||||||
|
func GetSQLiteDB(dbFile string) (*sql.DB, bool, error) {
|
||||||
|
return GetGenericDB("sqlite3", dbFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RowToArray is a convenience function, typically not called directly, which maps a
|
||||||
|
// single read database row into a NullString
|
||||||
|
func RowToArray(rows *sql.Rows, columns []string) []CellData {
|
||||||
|
buff := make([]interface{}, len(columns))
|
||||||
|
data := make([]CellData, len(columns))
|
||||||
|
for i, _ := range buff {
|
||||||
|
buff[i] = data[i].NullString()
|
||||||
|
}
|
||||||
|
rows.Scan(buff...)
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
// ScanRowsToArrays is a convenience function, typically not called directly, which maps rows
|
||||||
|
// already read from the databse into arrays of NullString
|
||||||
|
func ScanRowsToArrays(rows *sql.Rows, on_row func([]CellData) error) error {
|
||||||
|
columns, _ := rows.Columns()
|
||||||
|
for rows.Next() {
|
||||||
|
arr := RowToArray(rows, columns)
|
||||||
|
err := on_row(arr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func rowToMap(row []CellData, columns []string) map[string]CellData {
|
||||||
|
m := make(map[string]CellData)
|
||||||
|
for k, data_col := range row {
|
||||||
|
m[columns[k]] = data_col
|
||||||
|
}
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
// ScanRowsToMaps is a convenience function, typically not called directly, which maps rows
|
||||||
|
// already read from the databse into RowMap entries.
|
||||||
|
func ScanRowsToMaps(rows *sql.Rows, on_row func(RowMap) error) error {
|
||||||
|
columns, _ := rows.Columns()
|
||||||
|
err := ScanRowsToArrays(rows, func(arr []CellData) error {
|
||||||
|
m := rowToMap(arr, columns)
|
||||||
|
err := on_row(m)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryRowsMap is a convenience function allowing querying a result set while poviding a callback
|
||||||
|
// function activated per read row.
|
||||||
|
func QueryRowsMap(db *sql.DB, query string, on_row func(RowMap) error, args ...interface{}) (err error) {
|
||||||
|
defer func() {
|
||||||
|
if derr := recover(); derr != nil {
|
||||||
|
err = fmt.Errorf("QueryRowsMap unexpected error: %+v", derr)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
var rows *sql.Rows
|
||||||
|
rows, err = db.Query(query, args...)
|
||||||
|
if rows != nil {
|
||||||
|
defer rows.Close()
|
||||||
|
}
|
||||||
|
if err != nil && err != sql.ErrNoRows {
|
||||||
|
return log.Errore(err)
|
||||||
|
}
|
||||||
|
err = ScanRowsToMaps(rows, on_row)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// queryResultData returns a raw array of rows for a given query, optionally reading and returning column names
|
||||||
|
func queryResultData(db *sql.DB, query string, retrieveColumns bool, args ...interface{}) (resultData ResultData, columns []string, err error) {
|
||||||
|
defer func() {
|
||||||
|
if derr := recover(); derr != nil {
|
||||||
|
err = errors.New(fmt.Sprintf("QueryRowsMap unexpected error: %+v", derr))
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
var rows *sql.Rows
|
||||||
|
rows, err = db.Query(query, args...)
|
||||||
|
defer rows.Close()
|
||||||
|
if err != nil && err != sql.ErrNoRows {
|
||||||
|
return EmptyResultData, columns, err
|
||||||
|
}
|
||||||
|
if retrieveColumns {
|
||||||
|
// Don't pay if you don't want to
|
||||||
|
columns, _ = rows.Columns()
|
||||||
|
}
|
||||||
|
resultData = ResultData{}
|
||||||
|
err = ScanRowsToArrays(rows, func(rowData []CellData) error {
|
||||||
|
resultData = append(resultData, rowData)
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
return resultData, columns, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryResultData returns a raw array of rows
|
||||||
|
func QueryResultData(db *sql.DB, query string, args ...interface{}) (ResultData, error) {
|
||||||
|
resultData, _, err := queryResultData(db, query, false, args...)
|
||||||
|
return resultData, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryResultDataNamed returns a raw array of rows, with column names
|
||||||
|
func QueryNamedResultData(db *sql.DB, query string, args ...interface{}) (NamedResultData, error) {
|
||||||
|
resultData, columns, err := queryResultData(db, query, true, args...)
|
||||||
|
return NamedResultData{Columns: columns, Data: resultData}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryRowsMapBuffered reads data from the database into a buffer, and only then applies the given function per row.
|
||||||
|
// This allows the application to take its time with processing the data, albeit consuming as much memory as required by
|
||||||
|
// the result set.
|
||||||
|
func QueryRowsMapBuffered(db *sql.DB, query string, on_row func(RowMap) error, args ...interface{}) error {
|
||||||
|
resultData, columns, err := queryResultData(db, query, true, args...)
|
||||||
|
if err != nil {
|
||||||
|
// Already logged
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, row := range resultData {
|
||||||
|
err = on_row(rowToMap(row, columns))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExecNoPrepare executes given query using given args on given DB, without using prepared statements.
|
||||||
|
func ExecNoPrepare(db *sql.DB, query string, args ...interface{}) (res sql.Result, err error) {
|
||||||
|
defer func() {
|
||||||
|
if derr := recover(); derr != nil {
|
||||||
|
err = errors.New(fmt.Sprintf("ExecNoPrepare unexpected error: %+v", derr))
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
res, err = db.Exec(query, args...)
|
||||||
|
if err != nil {
|
||||||
|
log.Errore(err)
|
||||||
|
}
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExecQuery executes given query using given args on given DB. It will safele prepare, execute and close
|
||||||
|
// the statement.
|
||||||
|
func execInternal(silent bool, db *sql.DB, query string, args ...interface{}) (res sql.Result, err error) {
|
||||||
|
defer func() {
|
||||||
|
if derr := recover(); derr != nil {
|
||||||
|
err = errors.New(fmt.Sprintf("execInternal unexpected error: %+v", derr))
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
var stmt *sql.Stmt
|
||||||
|
stmt, err = db.Prepare(query)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer stmt.Close()
|
||||||
|
res, err = stmt.Exec(args...)
|
||||||
|
if err != nil && !silent {
|
||||||
|
log.Errore(err)
|
||||||
|
}
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exec executes given query using given args on given DB. It will safele prepare, execute and close
|
||||||
|
// the statement.
|
||||||
|
func Exec(db *sql.DB, query string, args ...interface{}) (sql.Result, error) {
|
||||||
|
return execInternal(false, db, query, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExecSilently acts like Exec but does not report any error
|
||||||
|
func ExecSilently(db *sql.DB, query string, args ...interface{}) (sql.Result, error) {
|
||||||
|
return execInternal(true, db, query, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func InClauseStringValues(terms []string) string {
|
||||||
|
quoted := []string{}
|
||||||
|
for _, s := range terms {
|
||||||
|
quoted = append(quoted, fmt.Sprintf("'%s'", strings.Replace(s, ",", "''", -1)))
|
||||||
|
}
|
||||||
|
return strings.Join(quoted, ", ")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert variable length arguments into arguments array
|
||||||
|
func Args(args ...interface{}) []interface{} {
|
||||||
|
return args
|
||||||
|
}
|
||||||
|
|
||||||
|
func NilIfZero(i int64) interface{} {
|
||||||
|
if i == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
||||||
|
func ScanTable(db *sql.DB, tableName string) (NamedResultData, error) {
|
||||||
|
query := fmt.Sprintf("select * from %s", tableName)
|
||||||
|
return QueryNamedResultData(db, query)
|
||||||
|
}
|
||||||
|
|
||||||
|
func WriteTable(db *sql.DB, tableName string, data NamedResultData) (err error) {
|
||||||
|
if len(data.Data) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if len(data.Columns) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
placeholders := make([]string, len(data.Columns))
|
||||||
|
for i := range placeholders {
|
||||||
|
placeholders[i] = "?"
|
||||||
|
}
|
||||||
|
query := fmt.Sprintf(
|
||||||
|
`replace into %s (%s) values (%s)`,
|
||||||
|
tableName,
|
||||||
|
strings.Join(data.Columns, ","),
|
||||||
|
strings.Join(placeholders, ","),
|
||||||
|
)
|
||||||
|
for _, rowData := range data.Data {
|
||||||
|
if _, execErr := db.Exec(query, rowData.Args()...); execErr != nil {
|
||||||
|
err = execErr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
76
vendor/github.com/openark/golib/tests/spec.go
generated
vendored
Normal file
76
vendor/github.com/openark/golib/tests/spec.go
generated
vendored
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
package tests
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Spec is an access point to test Expections
|
||||||
|
type Spec struct {
|
||||||
|
t *testing.T
|
||||||
|
}
|
||||||
|
|
||||||
|
// S generates a spec. You will want to use it once in a test file, once in a test or once per each check
|
||||||
|
func S(t *testing.T) *Spec {
|
||||||
|
return &Spec{t: t}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExpectNil expects given value to be nil, or errors
|
||||||
|
func (spec *Spec) ExpectNil(actual interface{}) {
|
||||||
|
if actual == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
spec.t.Errorf("Expected %+v to be nil", actual)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExpectNotNil expects given value to be not nil, or errors
|
||||||
|
func (spec *Spec) ExpectNotNil(actual interface{}) {
|
||||||
|
if actual != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
spec.t.Errorf("Expected %+v to be not nil", actual)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExpectEquals expects given values to be equal (comparison via `==`), or errors
|
||||||
|
func (spec *Spec) ExpectEquals(actual, value interface{}) {
|
||||||
|
if actual == value {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
spec.t.Errorf("Expected:\n[[[%+v]]]\n- got:\n[[[%+v]]]", value, actual)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExpectNotEquals expects given values to be nonequal (comparison via `==`), or errors
|
||||||
|
func (spec *Spec) ExpectNotEquals(actual, value interface{}) {
|
||||||
|
if !(actual == value) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
spec.t.Errorf("Expected not %+v", value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExpectEqualsAny expects given actual to equal (comparison via `==`) at least one of given values, or errors
|
||||||
|
func (spec *Spec) ExpectEqualsAny(actual interface{}, values ...interface{}) {
|
||||||
|
for _, value := range values {
|
||||||
|
if actual == value {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
spec.t.Errorf("Expected %+v to equal any of given values", actual)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExpectNotEqualsAny expects given actual to be nonequal (comparison via `==`)tp any of given values, or errors
|
||||||
|
func (spec *Spec) ExpectNotEqualsAny(actual interface{}, values ...interface{}) {
|
||||||
|
for _, value := range values {
|
||||||
|
if actual == value {
|
||||||
|
spec.t.Errorf("Expected not %+v", value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExpectFalse expects given values to be false, or errors
|
||||||
|
func (spec *Spec) ExpectFalse(actual interface{}) {
|
||||||
|
spec.ExpectEquals(actual, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExpectTrue expects given values to be true, or errors
|
||||||
|
func (spec *Spec) ExpectTrue(actual interface{}) {
|
||||||
|
spec.ExpectEquals(actual, true)
|
||||||
|
}
|
103
vendor/github.com/openark/golib/util/text.go
generated
vendored
Normal file
103
vendor/github.com/openark/golib/util/text.go
generated
vendored
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2015 Shlomi Noach.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package util
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
TabulateLeft = 0
|
||||||
|
TabulateRight = 1
|
||||||
|
)
|
||||||
|
|
||||||
|
// ParseSimpleTime parses input in the format 7s, 55m, 3h, 31d, 4w (second, minute, hour, day, week)
|
||||||
|
// The time.ParseDuration() function should have done this, but it does not support "d" and "w" extensions.
|
||||||
|
func SimpleTimeToSeconds(simpleTime string) (int, error) {
|
||||||
|
if matched, _ := regexp.MatchString("^[0-9]+s$", simpleTime); matched {
|
||||||
|
i, _ := strconv.Atoi(simpleTime[0 : len(simpleTime)-1])
|
||||||
|
return i, nil
|
||||||
|
}
|
||||||
|
if matched, _ := regexp.MatchString("^[0-9]+m$", simpleTime); matched {
|
||||||
|
i, _ := strconv.Atoi(simpleTime[0 : len(simpleTime)-1])
|
||||||
|
return i * 60, nil
|
||||||
|
}
|
||||||
|
if matched, _ := regexp.MatchString("^[0-9]+h$", simpleTime); matched {
|
||||||
|
i, _ := strconv.Atoi(simpleTime[0 : len(simpleTime)-1])
|
||||||
|
return i * 60 * 60, nil
|
||||||
|
}
|
||||||
|
if matched, _ := regexp.MatchString("^[0-9]+d$", simpleTime); matched {
|
||||||
|
i, _ := strconv.Atoi(simpleTime[0 : len(simpleTime)-1])
|
||||||
|
return i * 60 * 60 * 24, nil
|
||||||
|
}
|
||||||
|
if matched, _ := regexp.MatchString("^[0-9]+w$", simpleTime); matched {
|
||||||
|
i, _ := strconv.Atoi(simpleTime[0 : len(simpleTime)-1])
|
||||||
|
return i * 60 * 60 * 24 * 7, nil
|
||||||
|
}
|
||||||
|
return 0, errors.New(fmt.Sprintf("Cannot parse simple time: %s", simpleTime))
|
||||||
|
}
|
||||||
|
|
||||||
|
func Tabulate(lines []string, separator string, outputSeparator string, directionFlags ...int) (result []string) {
|
||||||
|
tokens := make([][]string, 0)
|
||||||
|
widths := make([][]int, 0)
|
||||||
|
countColumns := 0
|
||||||
|
for _, line := range lines {
|
||||||
|
lineTokens := strings.Split(line, separator)
|
||||||
|
lineWidths := make([]int, len(lineTokens))
|
||||||
|
for i := range lineTokens {
|
||||||
|
lineWidths[i] = len(lineTokens[i])
|
||||||
|
}
|
||||||
|
tokens = append(tokens, lineTokens)
|
||||||
|
widths = append(widths, lineWidths)
|
||||||
|
if len(lineTokens) > countColumns {
|
||||||
|
countColumns = len(lineTokens)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
columnWidths := make([]int, countColumns)
|
||||||
|
for _, lineTokens := range tokens {
|
||||||
|
for col, token := range lineTokens {
|
||||||
|
if len(token) > columnWidths[col] {
|
||||||
|
columnWidths[col] = len(token)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, lineTokens := range tokens {
|
||||||
|
resultRow := ""
|
||||||
|
for col := 0; col < countColumns; col++ {
|
||||||
|
token := ""
|
||||||
|
if col < len(lineTokens) {
|
||||||
|
token = lineTokens[col]
|
||||||
|
}
|
||||||
|
format := fmt.Sprintf("%%-%ds", columnWidths[col]) // format left
|
||||||
|
if col < len(directionFlags) && directionFlags[col] == TabulateRight {
|
||||||
|
format = fmt.Sprintf("%%%ds", columnWidths[col])
|
||||||
|
}
|
||||||
|
formattedToken := fmt.Sprintf(format, token)
|
||||||
|
if col == 0 {
|
||||||
|
resultRow = formattedToken
|
||||||
|
} else {
|
||||||
|
resultRow = fmt.Sprintf("%s%s%s", resultRow, outputSeparator, formattedToken)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result = append(result, resultRow)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
88
vendor/github.com/openark/golib/util/text_test.go
generated
vendored
Normal file
88
vendor/github.com/openark/golib/util/text_test.go
generated
vendored
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2014 Outbrain Inc.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package util
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
test "github.com/openark/golib/tests"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTabulate(t *testing.T) {
|
||||||
|
{
|
||||||
|
text := strings.TrimSpace(`
|
||||||
|
a,b,c
|
||||||
|
d,e,f
|
||||||
|
g,h,i
|
||||||
|
`)
|
||||||
|
|
||||||
|
tabulated := Tabulate(strings.Split(text, "\n"), ",", ",")
|
||||||
|
expected := strings.Split(text, "\n")
|
||||||
|
test.S(t).ExpectTrue(reflect.DeepEqual(tabulated, expected))
|
||||||
|
}
|
||||||
|
{
|
||||||
|
text := strings.TrimSpace(`
|
||||||
|
a,b,c
|
||||||
|
d,e,f
|
||||||
|
g,h,i
|
||||||
|
`)
|
||||||
|
|
||||||
|
tabulated := Tabulate(strings.Split(text, "\n"), ",", "|")
|
||||||
|
expected := []string{
|
||||||
|
"a|b|c",
|
||||||
|
"d|e|f",
|
||||||
|
"g|h|i",
|
||||||
|
}
|
||||||
|
test.S(t).ExpectTrue(reflect.DeepEqual(tabulated, expected))
|
||||||
|
}
|
||||||
|
{
|
||||||
|
text := strings.TrimSpace(`
|
||||||
|
a,20,c
|
||||||
|
d,e,100
|
||||||
|
0000,h,i
|
||||||
|
`)
|
||||||
|
|
||||||
|
tabulated := Tabulate(strings.Split(text, "\n"), ",", "|")
|
||||||
|
expected := []string{
|
||||||
|
"a |20|c ",
|
||||||
|
"d |e |100",
|
||||||
|
"0000|h |i ",
|
||||||
|
}
|
||||||
|
test.S(t).ExpectTrue(reflect.DeepEqual(tabulated, expected))
|
||||||
|
}
|
||||||
|
{
|
||||||
|
text := strings.TrimSpace(`
|
||||||
|
a,20,c
|
||||||
|
d,1,100
|
||||||
|
0000,3,i
|
||||||
|
`)
|
||||||
|
|
||||||
|
tabulated := Tabulate(strings.Split(text, "\n"), ",", "|", TabulateLeft, TabulateRight, TabulateRight)
|
||||||
|
expected := []string{
|
||||||
|
"a |20| c",
|
||||||
|
"d | 1|100",
|
||||||
|
"0000| 3| i",
|
||||||
|
}
|
||||||
|
|
||||||
|
test.S(t).ExpectTrue(reflect.DeepEqual(tabulated, expected))
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user