diff --git a/helper/builtinplugins/builtin.go b/helper/builtinplugins/builtin.go index 40f3b130730e..2f956c8c2461 100644 --- a/helper/builtinplugins/builtin.go +++ b/helper/builtinplugins/builtin.go @@ -2,6 +2,7 @@ package builtinplugins import ( "github.com/hashicorp/vault/plugins/database/cassandra" + "github.com/hashicorp/vault/plugins/database/hana" "github.com/hashicorp/vault/plugins/database/mongodb" "github.com/hashicorp/vault/plugins/database/mssql" "github.com/hashicorp/vault/plugins/database/mysql" @@ -22,6 +23,7 @@ var plugins map[string]BuiltinFactory = map[string]BuiltinFactory{ "mssql-database-plugin": mssql.New, "cassandra-database-plugin": cassandra.New, "mongodb-database-plugin": mongodb.New, + "hana-database-plugin": hana.New, } func Get(name string) (BuiltinFactory, bool) { diff --git a/plugins/database/hana/hana-database-plugin/main.go b/plugins/database/hana/hana-database-plugin/main.go new file mode 100644 index 000000000000..a76d49c9e38b --- /dev/null +++ b/plugins/database/hana/hana-database-plugin/main.go @@ -0,0 +1,21 @@ +package main + +import ( + "log" + "os" + + "github.com/hashicorp/vault/helper/pluginutil" + "github.com/hashicorp/vault/plugins/database/hana" +) + +func main() { + apiClientMeta := &pluginutil.APIClientMeta{} + flags := apiClientMeta.FlagSet() + flags.Parse(os.Args) + + err := hana.Run(apiClientMeta.GetTLSConfig()) + if err != nil { + log.Println(err) + os.Exit(1) + } +} diff --git a/plugins/database/hana/hana.go b/plugins/database/hana/hana.go new file mode 100644 index 000000000000..aa2b53d65084 --- /dev/null +++ b/plugins/database/hana/hana.go @@ -0,0 +1,283 @@ +package hana + +import ( + "database/sql" + "fmt" + "strings" + "time" + + _ "github.com/SAP/go-hdb/driver" + "github.com/hashicorp/vault/api" + "github.com/hashicorp/vault/builtin/logical/database/dbplugin" + "github.com/hashicorp/vault/helper/strutil" + "github.com/hashicorp/vault/plugins" + "github.com/hashicorp/vault/plugins/helper/database/connutil" + "github.com/hashicorp/vault/plugins/helper/database/credsutil" + "github.com/hashicorp/vault/plugins/helper/database/dbutil" +) + +const ( + hanaTypeName = "hdb" +) + +// HANA is an implementation of Database interface +type HANA struct { + connutil.ConnectionProducer + credsutil.CredentialsProducer +} + +// New implements builtinplugins.BuiltinFactory +func New() (interface{}, error) { + connProducer := &connutil.SQLConnectionProducer{} + connProducer.Type = hanaTypeName + + credsProducer := &credsutil.SQLCredentialsProducer{ + DisplayNameLen: 32, + RoleNameLen: 20, + UsernameLen: 128, + Separator: "_", + } + + dbType := &HANA{ + ConnectionProducer: connProducer, + CredentialsProducer: credsProducer, + } + + return dbType, nil +} + +// Run instantiates a HANA object, and runs the RPC server for the plugin +func Run(apiTLSConfig *api.TLSConfig) error { + dbType, err := New() + if err != nil { + return err + } + + plugins.Serve(dbType.(*HANA), apiTLSConfig) + + return nil +} + +// Type returns the TypeName for this backend +func (h *HANA) Type() (string, error) { + return hanaTypeName, nil +} + +func (h *HANA) getConnection() (*sql.DB, error) { + db, err := h.Connection() + if err != nil { + return nil, err + } + + return db.(*sql.DB), nil +} + +// CreateUser generates the username/password on the underlying HANA secret backend +// as instructed by the CreationStatement provided. +func (h *HANA) CreateUser(statements dbplugin.Statements, usernameConfig dbplugin.UsernameConfig, expiration time.Time) (username string, password string, err error) { + // Grab the lock + h.Lock() + defer h.Unlock() + + // Get the connection + db, err := h.getConnection() + if err != nil { + return "", "", err + } + + if statements.CreationStatements == "" { + return "", "", dbutil.ErrEmptyCreationStatement + } + + // Generate username + username, err = h.GenerateUsername(usernameConfig) + if err != nil { + return "", "", err + } + + // HANA does not allow hyphens in usernames, and highly prefers capital letters + username = strings.Replace(username, "-", "_", -1) + username = strings.ToUpper(username) + + // Generate password + password, err = h.GeneratePassword() + if err != nil { + return "", "", err + } + // Most HANA configurations have password constraints + // Prefix with A1a to satisfy these constraints. User will be forced to change upon login + password = strings.Replace(password, "-", "_", -1) + password = "A1a" + password + + // If expiration is in the role SQL, HANA will deactivate the user when time is up, + // regardless of whether vault is alive to revoke lease + expirationStr, err := h.GenerateExpiration(expiration) + if err != nil { + return "", "", err + } + + // Start a transaction + tx, err := db.Begin() + if err != nil { + return "", "", err + } + defer tx.Rollback() + + // Execute each query + for _, query := range strutil.ParseArbitraryStringSlice(statements.CreationStatements, ";") { + query = strings.TrimSpace(query) + if len(query) == 0 { + continue + } + + stmt, err := tx.Prepare(dbutil.QueryHelper(query, map[string]string{ + "name": username, + "password": password, + "expiration": expirationStr, + })) + if err != nil { + return "", "", err + } + defer stmt.Close() + if _, err := stmt.Exec(); err != nil { + return "", "", err + } + } + + // Commit the transaction + if err := tx.Commit(); err != nil { + return "", "", err + } + + return username, password, nil +} + +// Renewing hana user just means altering user's valid until property +func (h *HANA) RenewUser(statements dbplugin.Statements, username string, expiration time.Time) error { + // Get connection + db, err := h.getConnection() + if err != nil { + return err + } + + // Start a transaction + tx, err := db.Begin() + if err != nil { + return err + } + defer tx.Rollback() + + // If expiration is in the role SQL, HANA will deactivate the user when time is up, + // regardless of whether vault is alive to revoke lease + expirationStr, err := h.GenerateExpiration(expiration) + if err != nil { + return err + } + + // Renew user's valid until property field + stmt, err := tx.Prepare("ALTER USER " + username + " VALID UNTIL " + "'" + expirationStr + "'") + if err != nil { + return err + } + defer stmt.Close() + if _, err := stmt.Exec(); err != nil { + return err + } + + // Commit the transaction + if err := tx.Commit(); err != nil { + return err + } + + return nil +} + +// Revoking hana user will deactivate user and try to perform a soft drop +func (h *HANA) RevokeUser(statements dbplugin.Statements, username string) error { + // default revoke will be a soft drop on user + if statements.RevocationStatements == "" { + return h.revokeUserDefault(username) + } + + // Get connection + db, err := h.getConnection() + if err != nil { + return err + } + + // Start a transaction + tx, err := db.Begin() + if err != nil { + return err + } + defer tx.Rollback() + + // Execute each query + for _, query := range strutil.ParseArbitraryStringSlice(statements.RevocationStatements, ";") { + query = strings.TrimSpace(query) + if len(query) == 0 { + continue + } + + stmt, err := tx.Prepare(dbutil.QueryHelper(query, map[string]string{ + "name": username, + })) + if err != nil { + return err + } + defer stmt.Close() + if _, err := stmt.Exec(); err != nil { + return err + } + } + + // Commit the transaction + if err := tx.Commit(); err != nil { + return err + } + + return nil +} + +func (h *HANA) revokeUserDefault(username string) error { + // Get connection + db, err := h.getConnection() + if err != nil { + return err + } + + // Start a transaction + tx, err := db.Begin() + if err != nil { + return err + } + defer tx.Rollback() + + // Disable server login for user + disableStmt, err := tx.Prepare(fmt.Sprintf("ALTER USER %s DEACTIVATE USER NOW", username)) + if err != nil { + return err + } + defer disableStmt.Close() + if _, err := disableStmt.Exec(); err != nil { + return err + } + + // Invalidates current sessions and performs soft drop (drop if no dependencies) + // if hard drop is desired, custom revoke statements should be written for role + dropStmt, err := tx.Prepare(fmt.Sprintf("DROP USER %s RESTRICT", username)) + if err != nil { + return err + } + defer dropStmt.Close() + if _, err := dropStmt.Exec(); err != nil { + return err + } + + // Commit transaction + if err := tx.Commit(); err != nil { + return err + } + + return nil +} diff --git a/plugins/database/hana/hana_test.go b/plugins/database/hana/hana_test.go new file mode 100644 index 000000000000..7cff7f1f3ab3 --- /dev/null +++ b/plugins/database/hana/hana_test.go @@ -0,0 +1,167 @@ +package hana + +import ( + "database/sql" + "fmt" + "os" + "strings" + "testing" + "time" + + "github.com/hashicorp/vault/builtin/logical/database/dbplugin" + "github.com/hashicorp/vault/plugins/helper/database/connutil" +) + +func TestHANA_Initialize(t *testing.T) { + if os.Getenv("HANA_URL") == "" || os.Getenv("VAULT_ACC") != "1" { + t.SkipNow() + } + connURL := os.Getenv("HANA_URL") + + connectionDetails := map[string]interface{}{ + "connection_url": connURL, + } + + dbRaw, _ := New() + db := dbRaw.(*HANA) + + err := db.Initialize(connectionDetails, true) + if err != nil { + t.Fatalf("err: %s", err) + } + + connProducer := db.ConnectionProducer.(*connutil.SQLConnectionProducer) + if !connProducer.Initialized { + t.Fatal("Database should be initialized") + } + + err = db.Close() + if err != nil { + t.Fatalf("err: %s", err) + } +} + +// this test will leave a lingering user on the system +func TestHANA_CreateUser(t *testing.T) { + if os.Getenv("HANA_URL") == "" || os.Getenv("VAULT_ACC") != "1" { + t.SkipNow() + } + connURL := os.Getenv("HANA_URL") + + connectionDetails := map[string]interface{}{ + "connection_url": connURL, + } + + dbRaw, _ := New() + db := dbRaw.(*HANA) + + err := db.Initialize(connectionDetails, true) + if err != nil { + t.Fatalf("err: %s", err) + } + + usernameConfig := dbplugin.UsernameConfig{ + DisplayName: "test-test", + RoleName: "test-test", + } + + // Test with no configured Creation Statememt + _, _, err = db.CreateUser(dbplugin.Statements{}, usernameConfig, time.Now().Add(time.Hour)) + if err == nil { + t.Fatal("Expected error when no creation statement is provided") + } + + statements := dbplugin.Statements{ + CreationStatements: testHANARole, + } + + username, password, err := db.CreateUser(statements, usernameConfig, time.Now().Add(time.Hour)) + if err != nil { + t.Fatalf("err: %s", err) + } + + if err = testCredsExist(t, connURL, username, password); err != nil { + t.Fatalf("Could not connect with new credentials: %s", err) + } +} + +func TestHANA_RevokeUser(t *testing.T) { + if os.Getenv("HANA_URL") == "" || os.Getenv("VAULT_ACC") != "1" { + t.SkipNow() + } + connURL := os.Getenv("HANA_URL") + + connectionDetails := map[string]interface{}{ + "connection_url": connURL, + } + + dbRaw, _ := New() + db := dbRaw.(*HANA) + + err := db.Initialize(connectionDetails, true) + if err != nil { + t.Fatalf("err: %s", err) + } + + statements := dbplugin.Statements{ + CreationStatements: testHANARole, + } + + usernameConfig := dbplugin.UsernameConfig{ + DisplayName: "test-test", + RoleName: "test-test", + } + + // Test default revoke statememts + username, password, err := db.CreateUser(statements, usernameConfig, time.Now().Add(time.Hour)) + if err != nil { + t.Fatalf("err: %s", err) + } + if err = testCredsExist(t, connURL, username, password); err != nil { + t.Fatalf("Could not connect with new credentials: %s", err) + } + + err = db.RevokeUser(statements, username) + if err != nil { + t.Fatalf("err: %s", err) + } + if err := testCredsExist(t, connURL, username, password); err == nil { + t.Fatal("Credentials were not revoked") + } + + // Test custom revoke statememt + username, password, err = db.CreateUser(statements, usernameConfig, time.Now().Add(time.Hour)) + if err != nil { + t.Fatalf("err: %s", err) + } + if err = testCredsExist(t, connURL, username, password); err != nil { + t.Fatalf("Could not connect with new credentials: %s", err) + } + + statements.RevocationStatements = testHANADrop + err = db.RevokeUser(statements, username) + if err != nil { + t.Fatalf("err: %s", err) + } + if err := testCredsExist(t, connURL, username, password); err == nil { + t.Fatal("Credentials were not revoked") + } +} + +func testCredsExist(t testing.TB, connURL, username, password string) error { + // Log in with the new creds + parts := strings.Split(connURL, "@") + connURL = fmt.Sprintf("hdb://%s:%s@%s", username, password, parts[1]) + db, err := sql.Open("hdb", connURL) + if err != nil { + return err + } + defer db.Close() + return db.Ping() +} + +const testHANARole = ` +CREATE USER {{name}} PASSWORD {{password}} VALID UNTIL '{{expiration}}';` + +const testHANADrop = ` +DROP USER {{name}} CASCADE;` diff --git a/vendor/github.com/SAP/go-hdb/LICENSE b/vendor/github.com/SAP/go-hdb/LICENSE new file mode 100644 index 000000000000..ad410e113021 --- /dev/null +++ b/vendor/github.com/SAP/go-hdb/LICENSE @@ -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 {yyyy} {name of copyright owner} + + 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. \ No newline at end of file diff --git a/vendor/github.com/SAP/go-hdb/NOTICE b/vendor/github.com/SAP/go-hdb/NOTICE new file mode 100644 index 000000000000..de5f2eaa3a3d --- /dev/null +++ b/vendor/github.com/SAP/go-hdb/NOTICE @@ -0,0 +1,5 @@ +SAP HANA Database driver for the Go Programming Language +Copyright 2014 SAP SE + +This product includes software developed at +SAP SE (http://www.sap.com). \ No newline at end of file diff --git a/vendor/github.com/SAP/go-hdb/driver/bytes.go b/vendor/github.com/SAP/go-hdb/driver/bytes.go new file mode 100644 index 000000000000..1bb77bf35d3d --- /dev/null +++ b/vendor/github.com/SAP/go-hdb/driver/bytes.go @@ -0,0 +1,43 @@ +/* +Copyright 2017 SAP SE + +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 driver + +import ( + "database/sql/driver" +) + +// NullBytes represents an []byte that may be null. +// NullBytes implements the Scanner interface so +// it can be used as a scan destination, similar to NullString. +type NullBytes struct { + Bytes []byte + Valid bool // Valid is true if Bytes is not NULL +} + +// Scan implements the Scanner interface. +func (n *NullBytes) Scan(value interface{}) error { + n.Bytes, n.Valid = value.([]byte) + return nil +} + +// Value implements the driver Valuer interface. +func (n NullBytes) Value() (driver.Value, error) { + if !n.Valid { + return nil, nil + } + return n.Bytes, nil +} diff --git a/vendor/github.com/SAP/go-hdb/driver/converter.go b/vendor/github.com/SAP/go-hdb/driver/converter.go new file mode 100644 index 000000000000..354fc47ad370 --- /dev/null +++ b/vendor/github.com/SAP/go-hdb/driver/converter.go @@ -0,0 +1,335 @@ +/* +Copyright 2014 SAP SE + +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 driver + +import ( + "database/sql/driver" + "errors" + "fmt" + "math" + "reflect" + "time" + + p "github.com/SAP/go-hdb/internal/protocol" +) + +const ( + minTinyint = 0 + maxTinyint = math.MaxUint8 + minSmallint = math.MinInt16 + maxSmallint = math.MaxInt16 + minInteger = math.MinInt32 + maxInteger = math.MaxInt32 + minBigint = math.MinInt64 + maxBigint = math.MaxInt64 + maxReal = math.MaxFloat32 + maxDouble = math.MaxFloat64 +) + +// ErrorIntegerOutOfRange means that an integer exceeds the size of the hdb integer field. +var ErrIntegerOutOfRange = errors.New("integer out of range error") + +// ErrorIntegerOutOfRange means that a float exceeds the size of the hdb float field. +var ErrFloatOutOfRange = errors.New("float out of range error") + +var typeOfTime = reflect.TypeOf((*time.Time)(nil)).Elem() +var typeOfBytes = reflect.TypeOf((*[]byte)(nil)).Elem() + +func columnConverter(dt p.DataType) driver.ValueConverter { + + switch dt { + + default: + return dbUnknownType{} + case p.DtTinyint: + return dbTinyint + case p.DtSmallint: + return dbSmallint + case p.DtInt: + return dbInt + case p.DtBigint: + return dbBigint + case p.DtReal: + return dbReal + case p.DtDouble: + return dbDouble + case p.DtTime: + return dbTime + case p.DtDecimal: + return dbDecimal + case p.DtString: + return dbString + case p.DtBytes: + return dbBytes + case p.DtLob: + return dbLob + } +} + +// unknown type +type dbUnknownType struct{} + +var _ driver.ValueConverter = dbUnknownType{} //check that type implements interface + +func (t dbUnknownType) ConvertValue(v interface{}) (driver.Value, error) { + return nil, fmt.Errorf("column converter for data %v type %T is not implemented", v, v) +} + +// int types +var dbTinyint = dbIntType{min: minTinyint, max: maxTinyint} +var dbSmallint = dbIntType{min: minSmallint, max: maxSmallint} +var dbInt = dbIntType{min: minInteger, max: maxInteger} +var dbBigint = dbIntType{min: minBigint, max: maxBigint} + +type dbIntType struct { + min int64 + max int64 +} + +var _ driver.ValueConverter = dbIntType{} //check that type implements interface + +func (i dbIntType) ConvertValue(v interface{}) (driver.Value, error) { + + if v == nil { + return v, nil + } + + rv := reflect.ValueOf(v) + switch rv.Kind() { + + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + i64 := rv.Int() + if i64 > i.max || i64 < i.min { + return nil, ErrIntegerOutOfRange + } + return i64, nil + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + u64 := rv.Uint() + if u64 > uint64(i.max) { + return nil, ErrIntegerOutOfRange + } + return int64(u64), nil + case reflect.Ptr: + // indirect pointers + if rv.IsNil() { + return nil, nil + } + return i.ConvertValue(rv.Elem().Interface()) + } + + return nil, fmt.Errorf("unsupported integer conversion type error %T %v", v, v) +} + +//float types +var dbReal = dbFloatType{max: maxReal} +var dbDouble = dbFloatType{max: maxDouble} + +type dbFloatType struct { + max float64 +} + +var _ driver.ValueConverter = dbFloatType{} //check that type implements interface + +func (f dbFloatType) ConvertValue(v interface{}) (driver.Value, error) { + + if v == nil { + return v, nil + } + + rv := reflect.ValueOf(v) + switch rv.Kind() { + + case reflect.Float32, reflect.Float64: + f64 := rv.Float() + if math.Abs(f64) > f.max { + return nil, ErrFloatOutOfRange + } + return f64, nil + case reflect.Ptr: + // indirect pointers + if rv.IsNil() { + return nil, nil + } + return f.ConvertValue(rv.Elem().Interface()) + } + + return nil, fmt.Errorf("unsupported float conversion type error %T %v", v, v) +} + +//time +var dbTime = dbTimeType{} + +type dbTimeType struct{} + +var _ driver.ValueConverter = dbTimeType{} //check that type implements interface + +func (t dbTimeType) ConvertValue(v interface{}) (driver.Value, error) { + + if v == nil { + return nil, nil + } + + switch v := v.(type) { + + case time.Time: + return v, nil + } + + rv := reflect.ValueOf(v) + + switch rv.Kind() { + + case reflect.Ptr: + // indirect pointers + if rv.IsNil() { + return nil, nil + } + return t.ConvertValue(rv.Elem().Interface()) + } + + if rv.Type().ConvertibleTo(typeOfTime) { + tv := rv.Convert(typeOfTime) + return tv.Interface().(time.Time), nil + } + + return nil, fmt.Errorf("unsupported time conversion type error %T %v", v, v) +} + +//decimal +var dbDecimal = dbDecimalType{} + +type dbDecimalType struct{} + +var _ driver.ValueConverter = dbDecimalType{} //check that type implements interface + +func (d dbDecimalType) ConvertValue(v interface{}) (driver.Value, error) { + + if v == nil { + return nil, nil + } + + if v, ok := v.([]byte); ok { + return v, nil + } + + return nil, fmt.Errorf("unsupported decimal conversion type error %T %v", v, v) +} + +//string +var dbString = dbStringType{} + +type dbStringType struct{} + +var _ driver.ValueConverter = dbStringType{} //check that type implements interface + +func (d dbStringType) ConvertValue(v interface{}) (driver.Value, error) { + + if v == nil { + return v, nil + } + + switch v := v.(type) { + + case string, []byte: + return v, nil + } + + rv := reflect.ValueOf(v) + + switch rv.Kind() { + + case reflect.String: + return rv.String(), nil + + case reflect.Slice: + if rv.Type() == typeOfBytes { + return rv.Bytes(), nil + } + + case reflect.Ptr: + // indirect pointers + if rv.IsNil() { + return nil, nil + } + return d.ConvertValue(rv.Elem().Interface()) + } + + if rv.Type().ConvertibleTo(typeOfBytes) { + bv := rv.Convert(typeOfBytes) + return bv.Interface().([]byte), nil + } + + return nil, fmt.Errorf("unsupported character conversion type error %T %v", v, v) +} + +//bytes +var dbBytes = dbBytesType{} + +type dbBytesType struct{} + +var _ driver.ValueConverter = dbBytesType{} //check that type implements interface + +func (d dbBytesType) ConvertValue(v interface{}) (driver.Value, error) { + + if v == nil { + return v, nil + } + + if v, ok := v.([]byte); ok { + return v, nil + } + + rv := reflect.ValueOf(v) + + switch rv.Kind() { + + case reflect.Slice: + if rv.Type() == typeOfBytes { + return rv.Bytes(), nil + } + + case reflect.Ptr: + // indirect pointers + if rv.IsNil() { + return nil, nil + } + return d.ConvertValue(rv.Elem().Interface()) + } + + if rv.Type().ConvertibleTo(typeOfBytes) { + bv := rv.Convert(typeOfBytes) + return bv.Interface().([]byte), nil + } + + return nil, fmt.Errorf("unsupported bytes conversion type error %T %v", v, v) +} + +//lob +var dbLob = dbLobType{} + +type dbLobType struct{} + +var _ driver.ValueConverter = dbLobType{} //check that type implements interface + +func (d dbLobType) ConvertValue(v interface{}) (driver.Value, error) { + + if v, ok := v.(int64); ok { + return v, nil + } + + return nil, fmt.Errorf("unsupported lob conversion type error %T %v", v, v) +} diff --git a/vendor/github.com/SAP/go-hdb/driver/decimal.go b/vendor/github.com/SAP/go-hdb/driver/decimal.go new file mode 100644 index 000000000000..65b728bbaa84 --- /dev/null +++ b/vendor/github.com/SAP/go-hdb/driver/decimal.go @@ -0,0 +1,377 @@ +/* +Copyright 2014 SAP SE + +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 driver + +import ( + "database/sql/driver" + "errors" + "fmt" + "math" + "math/big" + "sync" +) + +//bigint word size (*--> src/pkg/math/big/arith.go) +const ( + // Compute the size _S of a Word in bytes. + _m = ^big.Word(0) + _logS = _m>>8&1 + _m>>16&1 + _m>>32&1 + _S = 1 << _logS +) + +const ( + // http://en.wikipedia.org/wiki/Decimal128_floating-point_format + dec128Digits = 34 + dec128Bias = 6176 + dec128MinExp = -6176 + dec128MaxExp = 6111 +) + +const ( + decimalSize = 16 //number of bytes +) + +var natZero = big.NewInt(0) +var natOne = big.NewInt(1) +var natTen = big.NewInt(10) + +var nat = []*big.Int{ + natOne, //10^0 + natTen, //10^1 + big.NewInt(100), //10^2 + big.NewInt(1000), //10^3 + big.NewInt(10000), //10^4 + big.NewInt(100000), //10^5 + big.NewInt(1000000), //10^6 + big.NewInt(10000000), //10^7 + big.NewInt(100000000), //10^8 + big.NewInt(1000000000), //10^9 + big.NewInt(10000000000), //10^10 +} + +const lg10 = math.Ln10 / math.Ln2 // ~log2(10) + +var maxDecimal = new(big.Int).SetBytes([]byte{0x01, 0xED, 0x09, 0xBE, 0xAD, 0x87, 0xC0, 0x37, 0x8D, 0x8E, 0x63, 0xFF, 0xFF, 0xFF, 0xFF}) + +type decFlags byte + +const ( + dfNotExact decFlags = 1 << iota + dfOverflow + dfUnderflow +) + +// ErrDecimalOutOfRange means that a big.Rat exceeds the size of hdb decimal fields. +var ErrDecimalOutOfRange = errors.New("decimal out of range error") + +// big.Int free list +var bigIntFree = sync.Pool{ + New: func() interface{} { return new(big.Int) }, +} + +// big.Rat free list +var bigRatFree = sync.Pool{ + New: func() interface{} { return new(big.Rat) }, +} + +// A Decimal is the driver representation of a database decimal field value as big.Rat. +type Decimal big.Rat + +// Scan implements the database/sql/Scanner interface. +func (d *Decimal) Scan(src interface{}) error { + + b, ok := src.([]byte) + if !ok { + return fmt.Errorf("decimal: invalid data type %T", src) + } + + if len(b) != decimalSize { + return fmt.Errorf("decimal: invalid size %d of %v - %d expected", len(b), b, decimalSize) + } + + if (b[15] & 0x60) == 0x60 { + return fmt.Errorf("decimal: format (infinity, nan, ...) not supported : %v", b) + } + + v := (*big.Rat)(d) + p := v.Num() + q := v.Denom() + + neg, exp := decodeDecimal(b, p) + + switch { + case exp < 0: + q.Set(exp10(exp * -1)) + case exp == 0: + q.Set(natOne) + case exp > 0: + p.Mul(p, exp10(exp)) + q.Set(natOne) + } + + if neg { + v.Neg(v) + } + return nil +} + +// Value implements the database/sql/Valuer interface. +func (d Decimal) Value() (driver.Value, error) { + m := bigIntFree.Get().(*big.Int) + neg, exp, df := convertRatToDecimal((*big.Rat)(&d), m, dec128Digits, dec128MinExp, dec128MaxExp) + + var v driver.Value + var err error + + switch { + default: + v, err = encodeDecimal(m, neg, exp) + case df&dfUnderflow != 0: // set to zero + m.Set(natZero) + v, err = encodeDecimal(m, false, 0) + case df&dfOverflow != 0: + err = ErrDecimalOutOfRange + } + + // performance (avoid expensive defer) + bigIntFree.Put(m) + + return v, err +} + +func convertRatToDecimal(x *big.Rat, m *big.Int, digits, minExp, maxExp int) (bool, int, decFlags) { + + neg := x.Sign() < 0 //store sign + + if x.Num().Cmp(natZero) == 0 { // zero + m.Set(natZero) + return neg, 0, 0 + } + + c := bigRatFree.Get().(*big.Rat).Abs(x) // copy && abs + a := c.Num() + b := c.Denom() + + exp, shift := 0, 0 + + if c.IsInt() { + exp = digits10(a) - 1 + } else { + shift = digits10(a) - digits10(b) + switch { + case shift < 0: + a.Mul(a, exp10(shift*-1)) + case shift > 0: + b.Mul(b, exp10(shift)) + } + if a.Cmp(b) == -1 { + exp = shift - 1 + } else { + exp = shift + } + } + + var df decFlags + + switch { + default: + exp = max(exp-digits+1, minExp) + case exp < minExp: + df |= dfUnderflow + exp = exp - digits + 1 + } + + if exp > maxExp { + df |= dfOverflow + } + + shift = exp - shift + switch { + case shift < 0: + a.Mul(a, exp10(shift*-1)) + case exp > 0: + b.Mul(b, exp10(shift)) + } + + m.QuoRem(a, b, a) // reuse a as rest + if a.Cmp(natZero) != 0 { + // round (business >= 0.5 up) + df |= dfNotExact + if a.Add(a, a).Cmp(b) >= 0 { + m.Add(m, natOne) + if m.Cmp(exp10(digits)) == 0 { + shift := min(digits, maxExp-exp) + if shift < 1 { // overflow -> shift one at minimum + df |= dfOverflow + shift = 1 + } + m.Set(exp10(digits - shift)) + exp += shift + } + } + } + + // norm + for exp < maxExp { + a.QuoRem(m, natTen, b) // reuse a, b + if b.Cmp(natZero) != 0 { + break + } + m.Set(a) + exp++ + } + + // performance (avoid expensive defer) + bigRatFree.Put(c) + + return neg, exp, df +} + +func min(a, b int) int { + if a < b { + return a + } + return b +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} + +// performance: tested with reference work variable +// - but int.Set is expensive, so let's live with big.Int creation for n >= len(nat) +func exp10(n int) *big.Int { + if n < len(nat) { + return nat[n] + } + r := big.NewInt(int64(n)) + return r.Exp(natTen, r, nil) +} + +func digits10(p *big.Int) int { + k := p.BitLen() // 2^k <= p < 2^(k+1) - 1 + //i := int(float64(k) / lg10) //minimal digits base 10 + //i := int(float64(k) / lg10) //minimal digits base 10 + i := k * 100 / 332 + if i < 1 { + i = 1 + } + + for ; ; i++ { + if p.Cmp(exp10(i)) < 0 { + return i + } + } +} + +func decodeDecimal(b []byte, m *big.Int) (bool, int) { + + neg := (b[15] & 0x80) != 0 + exp := int((((uint16(b[15])<<8)|uint16(b[14]))<<1)>>2) - dec128Bias + + b14 := b[14] // save b[14] + b[14] &= 0x01 // keep the mantissa bit (rest: sign and exp) + + //most significand byte + msb := 14 + for msb > 0 { + if b[msb] != 0 { + break + } + msb-- + } + + //calc number of words + numWords := (msb / _S) + 1 + w := make([]big.Word, numWords) + + k := numWords - 1 + d := big.Word(0) + for i := msb; i >= 0; i-- { + d |= big.Word(b[i]) + if k*_S == i { + w[k] = d + k-- + d = 0 + } + d <<= 8 + } + b[14] = b14 // restore b[14] + m.SetBits(w) + return neg, exp +} + +func encodeDecimal(m *big.Int, neg bool, exp int) (driver.Value, error) { + + b := make([]byte, decimalSize) + + // little endian bigint words (significand) -> little endian db decimal format + j := 0 + for _, d := range m.Bits() { + for i := 0; i < 8; i++ { + b[j] = byte(d) + d >>= 8 + j++ + } + } + + exp += dec128Bias + b[14] |= (byte(exp) << 1) + b[15] = byte(uint16(exp) >> 7) + + if neg { + b[15] |= 0x80 + } + + return b, nil +} + +// NullDecimal represents an Decimal that may be null. +// NullDecimal implements the Scanner interface so +// it can be used as a scan destination, similar to NullString. +type NullDecimal struct { + Decimal *Decimal + Valid bool // Valid is true if Decimal is not NULL +} + +// Scan implements the Scanner interface. +func (n *NullDecimal) Scan(value interface{}) error { + var b []byte + + b, n.Valid = value.([]byte) + if !n.Valid { + return nil + } + if n.Decimal == nil { + return fmt.Errorf("invalid decimal value %v", n.Decimal) + } + return n.Decimal.Scan(b) +} + +// Value implements the driver Valuer interface. +func (n NullDecimal) Value() (driver.Value, error) { + if !n.Valid { + return nil, nil + } + if n.Decimal == nil { + return nil, fmt.Errorf("invalid decimal value %v", n.Decimal) + } + return n.Decimal.Value() +} diff --git a/vendor/github.com/SAP/go-hdb/driver/doc.go b/vendor/github.com/SAP/go-hdb/driver/doc.go new file mode 100644 index 000000000000..d61bb5a0c531 --- /dev/null +++ b/vendor/github.com/SAP/go-hdb/driver/doc.go @@ -0,0 +1,18 @@ +/* +Copyright 2014 SAP SE + +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 driver is a native Go SAP HANA driver implementation for the database/sql package. +package driver diff --git a/vendor/github.com/SAP/go-hdb/driver/driver.go b/vendor/github.com/SAP/go-hdb/driver/driver.go new file mode 100644 index 000000000000..cc1864498f91 --- /dev/null +++ b/vendor/github.com/SAP/go-hdb/driver/driver.go @@ -0,0 +1,617 @@ +/* +Copyright 2014 SAP SE + +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 driver + +import ( + "database/sql" + "database/sql/driver" + "encoding/binary" + "errors" + "fmt" + "io" + "regexp" + "sync" + + "github.com/SAP/go-hdb/driver/sqltrace" + + p "github.com/SAP/go-hdb/internal/protocol" +) + +// DriverVersion is the version number of the hdb driver. +const DriverVersion = "0.9" + +// DriverName is the driver name to use with sql.Open for hdb databases. +const DriverName = "hdb" + +func init() { + sql.Register(DriverName, &drv{}) +} + +var reBulk = regexp.MustCompile("(?i)^(\\s)*(bulk +)(.*)") + +func checkBulkInsert(sql string) (string, bool) { + if reBulk.MatchString(sql) { + return reBulk.ReplaceAllString(sql, "${3}"), true + } + return sql, false +} + +var reCall = regexp.MustCompile("(?i)^(\\s)*(call +)(.*)") + +func checkCallProcedure(sql string) bool { + return reCall.MatchString(sql) +} + +var errProcTableQuery = errors.New("Invalid procedure table query") + +// driver +type drv struct{} + +func (d *drv) Open(dsn string) (driver.Conn, error) { + return newConn(dsn) +} + +// database connection +type conn struct { + session *p.Session +} + +func newConn(dsn string) (driver.Conn, error) { + sessionPrm, err := parseDSN(dsn) + if err != nil { + return nil, err + } + session, err := p.NewSession(sessionPrm) + if err != nil { + return nil, err + } + return &conn{session: session}, nil +} + +func (c *conn) Prepare(query string) (driver.Stmt, error) { + if c.session.IsBad() { + return nil, driver.ErrBadConn + } + + prepareQuery, bulkInsert := checkBulkInsert(query) + + qt, id, parameterFieldSet, resultFieldSet, err := c.session.Prepare(prepareQuery) + if err != nil { + return nil, err + } + + if bulkInsert { + return newBulkInsertStmt(c.session, prepareQuery, id, parameterFieldSet) + } + return newStmt(qt, c.session, prepareQuery, id, parameterFieldSet, resultFieldSet) +} + +func (c *conn) Close() error { + c.session.Close() + return nil +} + +func (c *conn) Begin() (driver.Tx, error) { + if c.session.IsBad() { + return nil, driver.ErrBadConn + } + + if c.session.InTx() { + return nil, fmt.Errorf("nested transactions are not supported") + } + + c.session.SetInTx(true) + return newTx(c.session), nil +} + +// Exec implements the database/sql/driver/Execer interface. +func (c *conn) Exec(query string, args []driver.Value) (driver.Result, error) { + if c.session.IsBad() { + return nil, driver.ErrBadConn + } + + if len(args) != 0 { + return nil, driver.ErrSkip //fast path not possible (prepare needed) + } + + sqltrace.Traceln(query) + + return c.session.ExecDirect(query) +} + +// bug?: check args is performed indepently of queryer raising ErrSkip or not +// - leads to different behavior to prepare - stmt - execute default logic +// - seems to be the same for Execer interface + +// Queryer implements the database/sql/driver/Queryer interface. +func (c *conn) Query(query string, args []driver.Value) (driver.Rows, error) { + if c.session.IsBad() { + return nil, driver.ErrBadConn + } + + if len(args) != 0 { + return nil, driver.ErrSkip //fast path not possible (prepare needed) + } + + // direct execution of call procedure + // - returns no parameter metadata (sps 82) but only field values + // --> let's take the 'prepare way' for stored procedures + if checkCallProcedure(query) { + return nil, driver.ErrSkip + } + + sqltrace.Traceln(query) + + id, idx, ok := decodeTableQuery(query) + if ok { + r := procedureCallResultStore.get(id) + if r == nil { + return nil, fmt.Errorf("invalid procedure table query %s", query) + } + return r.tableRows(int(idx)) + } + + id, meta, values, attributes, err := c.session.QueryDirect(query) + if err != nil { + return nil, err + } + if id == 0 { // non select query + return noResult, nil + } + return newQueryResult(c.session, id, meta, values, attributes) +} + +//transaction +type tx struct { + session *p.Session +} + +func newTx(session *p.Session) *tx { + return &tx{ + session: session, + } +} + +func (t *tx) Commit() error { + if t.session.IsBad() { + return driver.ErrBadConn + } + + return t.session.Commit() +} + +func (t *tx) Rollback() error { + if t.session.IsBad() { + return driver.ErrBadConn + } + + return t.session.Rollback() +} + +//statement +type stmt struct { + qt p.QueryType + session *p.Session + query string + id uint64 + prmFieldSet *p.FieldSet + resultFieldSet *p.FieldSet +} + +func newStmt(qt p.QueryType, session *p.Session, query string, id uint64, prmFieldSet *p.FieldSet, resultFieldSet *p.FieldSet) (*stmt, error) { + return &stmt{qt: qt, session: session, query: query, id: id, prmFieldSet: prmFieldSet, resultFieldSet: resultFieldSet}, nil +} + +func (s *stmt) Close() error { + return s.session.DropStatementID(s.id) +} + +func (s *stmt) NumInput() int { + return s.prmFieldSet.NumInputField() +} + +func (s *stmt) Exec(args []driver.Value) (driver.Result, error) { + if s.session.IsBad() { + return nil, driver.ErrBadConn + } + + numField := s.prmFieldSet.NumInputField() + if len(args) != numField { + return nil, fmt.Errorf("invalid number of arguments %d - %d expected", len(args), numField) + } + + sqltrace.Tracef("%s %v", s.query, args) + + return s.session.Exec(s.id, s.prmFieldSet, args) +} + +func (s *stmt) Query(args []driver.Value) (driver.Rows, error) { + if s.session.IsBad() { + return nil, driver.ErrBadConn + } + + switch s.qt { + default: + rows, err := s.defaultQuery(args) + return rows, err + case p.QtProcedureCall: + rows, err := s.procedureCall(args) + return rows, err + } +} + +func (s *stmt) defaultQuery(args []driver.Value) (driver.Rows, error) { + + sqltrace.Tracef("%s %v", s.query, args) + + rid, values, attributes, err := s.session.Query(s.id, s.prmFieldSet, s.resultFieldSet, args) + if err != nil { + return nil, err + } + if rid == 0 { // non select query + return noResult, nil + } + return newQueryResult(s.session, rid, s.resultFieldSet, values, attributes) +} + +func (s *stmt) procedureCall(args []driver.Value) (driver.Rows, error) { + + sqltrace.Tracef("%s %v", s.query, args) + + fieldValues, tableResults, err := s.session.Call(s.id, s.prmFieldSet, args) + if err != nil { + return nil, err + } + + return newProcedureCallResult(s.session, s.prmFieldSet, fieldValues, tableResults) +} + +func (s *stmt) ColumnConverter(idx int) driver.ValueConverter { + return columnConverter(s.prmFieldSet.DataType(idx)) +} + +// bulk insert statement +type bulkInsertStmt struct { + session *p.Session + query string + id uint64 + parameterFieldSet *p.FieldSet + numArg int + args []driver.Value +} + +func newBulkInsertStmt(session *p.Session, query string, id uint64, parameterFieldSet *p.FieldSet) (*bulkInsertStmt, error) { + return &bulkInsertStmt{session: session, query: query, id: id, parameterFieldSet: parameterFieldSet, args: make([]driver.Value, 0)}, nil +} + +func (s *bulkInsertStmt) Close() error { + return s.session.DropStatementID(s.id) +} + +func (s *bulkInsertStmt) NumInput() int { + return -1 +} + +func (s *bulkInsertStmt) Exec(args []driver.Value) (driver.Result, error) { + if s.session.IsBad() { + return nil, driver.ErrBadConn + } + + sqltrace.Tracef("%s %v", s.query, args) + + if args == nil || len(args) == 0 { + return s.execFlush() + } + return s.execBuffer(args) +} + +func (s *bulkInsertStmt) execFlush() (driver.Result, error) { + + if s.numArg == 0 { + return driver.ResultNoRows, nil + } + + result, err := s.session.Exec(s.id, s.parameterFieldSet, s.args) + s.args = s.args[:0] + s.numArg = 0 + return result, err +} + +func (s *bulkInsertStmt) execBuffer(args []driver.Value) (driver.Result, error) { + + numField := s.parameterFieldSet.NumInputField() + if len(args) != numField { + return nil, fmt.Errorf("invalid number of arguments %d - %d expected", len(args), numField) + } + + var result driver.Result = driver.ResultNoRows + var err error + + if s.numArg == maxSmallint { // TODO: check why bigArgument count does not work + result, err = s.execFlush() + } + + s.args = append(s.args, args...) + s.numArg++ + + return result, err +} + +func (s *bulkInsertStmt) Query(args []driver.Value) (driver.Rows, error) { + return nil, fmt.Errorf("query not allowed in context of bulk insert statement %s", s.query) +} + +func (s *bulkInsertStmt) ColumnConverter(idx int) driver.ValueConverter { + return columnConverter(s.parameterFieldSet.DataType(idx)) +} + +// driver.Rows drop-in replacement if driver Query or QueryRow is used for statements that doesn't return rows +var noColumns = []string{} +var noResult = new(noResultType) + +type noResultType struct{} + +func (r *noResultType) Columns() []string { return noColumns } +func (r *noResultType) Close() error { return nil } +func (r *noResultType) Next(dest []driver.Value) error { return io.EOF } + +// query result +type queryResult struct { + session *p.Session + id uint64 + fieldSet *p.FieldSet + fieldValues *p.FieldValues + pos int + attrs p.PartAttributes + columns []string + lastErr error +} + +func newQueryResult(session *p.Session, id uint64, fieldSet *p.FieldSet, fieldValues *p.FieldValues, attrs p.PartAttributes) (driver.Rows, error) { + columns := make([]string, fieldSet.NumOutputField()) + if err := fieldSet.OutputNames(columns); err != nil { + return nil, err + } + + return &queryResult{ + session: session, + id: id, + fieldSet: fieldSet, + fieldValues: fieldValues, + attrs: attrs, + columns: columns, + }, nil +} + +func (r *queryResult) Columns() []string { + return r.columns +} + +func (r *queryResult) Close() error { + // if lastError is set, attrs are nil + if r.lastErr != nil { + return r.lastErr + } + + if !r.attrs.ResultsetClosed() { + return r.session.CloseResultsetID(r.id) + } + return nil +} + +func (r *queryResult) Next(dest []driver.Value) error { + if r.session.IsBad() { + return driver.ErrBadConn + } + + if r.pos >= r.fieldValues.NumRow() { + if r.attrs.LastPacket() { + return io.EOF + } + + var err error + + if r.fieldValues, r.attrs, err = r.session.FetchNext(r.id, r.fieldSet); err != nil { + r.lastErr = err //fieldValues and attrs are nil + return err + } + + if r.attrs.NoRows() { + return io.EOF + } + + r.pos = 0 + + } + + r.fieldValues.Row(r.pos, dest) + r.pos++ + + return nil +} + +//call result store +type callResultStore struct { + mu sync.RWMutex + store map[uint64]*procedureCallResult + cnt uint64 + free []uint64 +} + +func (s *callResultStore) get(k uint64) *procedureCallResult { + s.mu.RLock() + defer s.mu.RUnlock() + + if r, ok := s.store[k]; ok { + return r + } + return nil +} + +func (s *callResultStore) add(v *procedureCallResult) uint64 { + s.mu.Lock() + defer s.mu.Unlock() + + var k uint64 + + if s.free == nil || len(s.free) == 0 { + s.cnt++ + k = s.cnt + } else { + size := len(s.free) + k = s.free[size-1] + s.free = s.free[:size-1] + } + + if s.store == nil { + s.store = make(map[uint64]*procedureCallResult) + } + + s.store[k] = v + + return k +} + +func (s *callResultStore) del(k uint64) { + s.mu.Lock() + defer s.mu.Unlock() + + delete(s.store, k) + + if s.free == nil { + s.free = []uint64{k} + } else { + s.free = append(s.free, k) + } +} + +var procedureCallResultStore = new(callResultStore) + +//procedure call result +type procedureCallResult struct { + id uint64 + session *p.Session + fieldSet *p.FieldSet + fieldValues *p.FieldValues + _tableRows []driver.Rows + columns []string + eof error +} + +func newProcedureCallResult(session *p.Session, fieldSet *p.FieldSet, fieldValues *p.FieldValues, tableResults []*p.TableResult) (driver.Rows, error) { + + fieldIdx := fieldSet.NumOutputField() + columns := make([]string, fieldIdx+len(tableResults)) + if err := fieldSet.OutputNames(columns); err != nil { + return nil, err + } + + tableRows := make([]driver.Rows, len(tableResults)) + for i, tableResult := range tableResults { + var err error + + if tableRows[i], err = newQueryResult(session, tableResult.ID(), tableResult.FieldSet(), tableResult.FieldValues(), tableResult.Attrs()); err != nil { + return nil, err + } + + columns[fieldIdx] = fmt.Sprintf("table %d", i) + + fieldIdx++ + + } + + result := &procedureCallResult{ + session: session, + fieldSet: fieldSet, + fieldValues: fieldValues, + _tableRows: tableRows, + columns: columns, + } + id := procedureCallResultStore.add(result) + result.id = id + return result, nil +} + +func (r *procedureCallResult) Columns() []string { + return r.columns +} + +func (r *procedureCallResult) Close() error { + procedureCallResultStore.del(r.id) + return nil +} + +func (r *procedureCallResult) Next(dest []driver.Value) error { + if r.session.IsBad() { + return driver.ErrBadConn + } + + if r.eof != nil { + return r.eof + } + + if r.fieldValues.NumRow() == 0 && len(r._tableRows) == 0 { + r.eof = io.EOF + return r.eof + } + + if r.fieldValues.NumRow() != 0 { + r.fieldValues.Row(0, dest) + } + + i := r.fieldSet.NumOutputField() + for j := range r._tableRows { + dest[i] = encodeTableQuery(r.id, uint64(j)) + i++ + } + + r.eof = io.EOF + return nil +} + +func (r *procedureCallResult) tableRows(idx int) (driver.Rows, error) { + if idx >= len(r._tableRows) { + return nil, fmt.Errorf("table row index %d exceeds maximun %d", idx, len(r._tableRows)-1) + } + return r._tableRows[idx], nil +} + +// helper +const tableQueryPrefix = "@tq" + +func encodeTableQuery(id, idx uint64) string { + start := len(tableQueryPrefix) + b := make([]byte, start+8+8) + copy(b, tableQueryPrefix) + binary.LittleEndian.PutUint64(b[start:start+8], id) + binary.LittleEndian.PutUint64(b[start+8:start+8+8], idx) + return string(b) +} + +func decodeTableQuery(query string) (uint64, uint64, bool) { + size := len(query) + start := len(tableQueryPrefix) + if size != start+8+8 { + return 0, 0, false + } + if query[:start] != tableQueryPrefix { + return 0, 0, false + } + id := binary.LittleEndian.Uint64([]byte(query[start : start+8])) + idx := binary.LittleEndian.Uint64([]byte(query[start+8 : start+8+8])) + return id, idx, true +} diff --git a/vendor/github.com/SAP/go-hdb/driver/dsn.go b/vendor/github.com/SAP/go-hdb/driver/dsn.go new file mode 100644 index 000000000000..d26a2442af39 --- /dev/null +++ b/vendor/github.com/SAP/go-hdb/driver/dsn.go @@ -0,0 +1,87 @@ +/* +Copyright 2014 SAP SE + +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 driver + +import ( + "net/url" + "strconv" + + p "github.com/SAP/go-hdb/internal/protocol" +) + +// DSN query parameters. For parameter client locale see http://help.sap.com/hana/SAP_HANA_SQL_Command_Network_Protocol_Reference_en.pdf. +const ( + DSNLocale = "locale" // Client locale as described in the protocol reference. + DSNTimeout = "timeout" // Driver side connection timeout in seconds. +) +const ( + dsnBufferSize = "bufferSize" + dsnFetchSize = "fetchSize" +) + +// DSN query default values. +const ( + DSNDefaultTimeout = 300 // Default value connection timeout (300 seconds = 5 minutes). +) +const ( + dsnDefaultFetchSize = 128 +) + +/* +DSN is here for the purposes of documentation only. A DSN string is an URL string with the following format + + hdb://:@: + +and optional query parameters (see DSN query parameters and DSN query default values). + +Example: + + hdb://myuser:mypassword@localhost:30015?timeout=60 +*/ +type DSN string + +func parseDSN(dsn string) (*p.SessionPrm, error) { + + url, err := url.Parse(dsn) + if err != nil { + return nil, err + } + + prm := &p.SessionPrm{Host: url.Host} + + if url.User != nil { + prm.Username = url.User.Username() + prm.Password, _ = url.User.Password() + } + + values := url.Query() + + prm.BufferSize, _ = strconv.Atoi(values.Get(dsnBufferSize)) + + prm.FetchSize, err = strconv.Atoi(values.Get(dsnFetchSize)) + if err != nil { + prm.FetchSize = dsnDefaultFetchSize + } + prm.Timeout, err = strconv.Atoi(values.Get(DSNTimeout)) + if err != nil { + prm.Timeout = DSNDefaultTimeout + } + + prm.Locale = values.Get(DSNLocale) + + return prm, nil +} diff --git a/vendor/github.com/SAP/go-hdb/driver/error.go b/vendor/github.com/SAP/go-hdb/driver/error.go new file mode 100644 index 000000000000..c846c5a353c1 --- /dev/null +++ b/vendor/github.com/SAP/go-hdb/driver/error.go @@ -0,0 +1,32 @@ +/* +Copyright 2014 SAP SE + +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 driver + +import ( + p "github.com/SAP/go-hdb/internal/protocol" +) + +// Error represents errors send by the database server. +type Error interface { + Code() int // Code return the database error code. + Position() int // Position returns the start position of erroneous sql statements sent to the database server. + Level() p.ErrorLevel // Level return one of the database server predefined error levels. + Text() string // Text return the error description sent from database server. + IsWarning() bool // IsWarning returns true if the HDB error level equals 0. + IsError() bool // IsError returns true if the HDB error level equals 1. + IsFatal() bool // IsFatal returns true if the HDB error level equals 2. +} diff --git a/vendor/github.com/SAP/go-hdb/driver/identifier.go b/vendor/github.com/SAP/go-hdb/driver/identifier.go new file mode 100644 index 000000000000..a691ac88d4c6 --- /dev/null +++ b/vendor/github.com/SAP/go-hdb/driver/identifier.go @@ -0,0 +1,49 @@ +/* +Copyright 2014 SAP SE + +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 driver + +import ( + "crypto/rand" + "fmt" + "io" + "regexp" + "strconv" +) + +var reSimple = regexp.MustCompile("^[_A-Z][_#$A-Z0-9]*$") + +// Identifier in hdb SQL statements like schema or table name. +type Identifier string + +// Random Identifier returns a random Identifier prefixed by the prefix parameter. +// This function is used to generate database objects with random names for test and example code. +func RandomIdentifier(prefix string) Identifier { + b := make([]byte, 16) + if _, err := io.ReadFull(rand.Reader, b); err != nil { + panic(err.Error()) // rand should never fail + } + return Identifier(fmt.Sprintf(fmt.Sprintf("%s%x", prefix, b))) +} + +// String implements Stringer interface. +func (i Identifier) String() string { + s := string(i) + if reSimple.MatchString(s) { + return s + } + return strconv.Quote(s) +} diff --git a/vendor/github.com/SAP/go-hdb/driver/lob.go b/vendor/github.com/SAP/go-hdb/driver/lob.go new file mode 100644 index 000000000000..83557fa95d59 --- /dev/null +++ b/vendor/github.com/SAP/go-hdb/driver/lob.go @@ -0,0 +1,83 @@ +/* +Copyright 2014 SAP SE + +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 driver + +import ( + "database/sql/driver" + "errors" + "fmt" + "io" + + p "github.com/SAP/go-hdb/internal/protocol" +) + +// A Lob is the driver representation of a database large object field. +// A Lob object uses an io.Reader object as source for writing content to a database lob field. +// A Lob object uses an io.Writer object as destination for reading content from a database lob field. +// A Lob can be created by contructor method NewLob with io.Reader and io.Writer as parameters or +// created by new, setting io.Reader and io.Writer by SetReader and SetWriter methods. +type Lob struct { + rd io.Reader + wr io.Writer + writeDescr *p.LobWriteDescr +} + +// NewLob creates a new Lob instance with the io.Reader and io.Writer given as parameters. +func NewLob(rd io.Reader, wr io.Writer) *Lob { + return &Lob{rd: rd, wr: wr} +} + +// SetReader sets the io.Reader source for a lob field to be written to database. +func (l *Lob) SetReader(rd io.Reader) { + l.rd = rd +} + +// SetWriter sets the io.Writer destination for a lob field to be read from database. +func (l *Lob) SetWriter(wr io.Writer) { + l.wr = wr +} + +// Scan implements the database/sql/Scanner interface. +func (l *Lob) Scan(src interface{}) error { + + if l.wr == nil { + return errors.New("lob error: initial writer") + } + + ptr, ok := src.(int64) + if !ok { + return fmt.Errorf("lob: invalid pointer type %T", src) + } + + descr := p.PointerToLobReadDescr(ptr) + if err := descr.SetWriter(l.wr); err != nil { + return err + } + return nil +} + +// Value implements the database/sql/Valuer interface. +func (l *Lob) Value() (driver.Value, error) { + if l.rd == nil { + return nil, errors.New("lob error: initial reader") + } + if l.writeDescr == nil { + l.writeDescr = new(p.LobWriteDescr) + } + l.writeDescr.SetReader(l.rd) + return p.LobWriteDescrToPointer(l.writeDescr), nil +} diff --git a/vendor/github.com/SAP/go-hdb/driver/sqltrace/doc.go b/vendor/github.com/SAP/go-hdb/driver/sqltrace/doc.go new file mode 100644 index 000000000000..ac23bbc6f28a --- /dev/null +++ b/vendor/github.com/SAP/go-hdb/driver/sqltrace/doc.go @@ -0,0 +1,18 @@ +/* +Copyright 2014 SAP SE + +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 sqltrace implements driver sql trace functions. +package sqltrace diff --git a/vendor/github.com/SAP/go-hdb/driver/sqltrace/sqltrace.go b/vendor/github.com/SAP/go-hdb/driver/sqltrace/sqltrace.go new file mode 100644 index 000000000000..ecf80c6fa06e --- /dev/null +++ b/vendor/github.com/SAP/go-hdb/driver/sqltrace/sqltrace.go @@ -0,0 +1,78 @@ +/* +Copyright 2014 SAP SE + +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 sqltrace + +import ( + "flag" + "log" + "os" + "sync" +) + +type sqlTrace struct { + mu sync.RWMutex //protects field on + on bool + *log.Logger +} + +func newSqlTrace() *sqlTrace { + return &sqlTrace{ + Logger: log.New(os.Stdout, "hdb ", log.Ldate|log.Ltime|log.Lshortfile), + } +} + +var tracer = newSqlTrace() + +func init() { + flag.BoolVar(&tracer.on, "hdb.sqlTrace", false, "enabling hdb sql trace") +} + +// On returns if tracing methods output is active. +func On() bool { + tracer.mu.RLock() + on := tracer.on + tracer.mu.RUnlock() + return on +} + +// SetOn sets tracing methods output active or inactive. +func SetOn(on bool) { + tracer.mu.Lock() + tracer.on = on + tracer.mu.Unlock() +} + +// Trace calls trace logger Print method to print to the trace logger. +func Trace(v ...interface{}) { + if On() { + tracer.Print(v...) + } +} + +// Trace calls trace logger Printf method to print to the trace logger. +func Tracef(format string, v ...interface{}) { + if On() { + tracer.Printf(format, v...) + } +} + +// Traceln calls trace logger Println method to print to the trace logger. +func Traceln(v ...interface{}) { + if On() { + tracer.Println(v...) + } +} diff --git a/vendor/github.com/SAP/go-hdb/driver/time.go b/vendor/github.com/SAP/go-hdb/driver/time.go new file mode 100644 index 000000000000..3b3d8ac0124d --- /dev/null +++ b/vendor/github.com/SAP/go-hdb/driver/time.go @@ -0,0 +1,44 @@ +/* +Copyright 2014 SAP SE + +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 driver + +import ( + "database/sql/driver" + "time" +) + +// NullTime represents an time.Time that may be null. +// NullTime implements the Scanner interface so +// it can be used as a scan destination, similar to NullString. +type NullTime struct { + Time time.Time + Valid bool // Valid is true if Time is not NULL +} + +// Scan implements the Scanner interface. +func (n *NullTime) Scan(value interface{}) error { + n.Time, n.Valid = value.(time.Time) + return nil +} + +// Value implements the driver Valuer interface. +func (n NullTime) Value() (driver.Value, error) { + if !n.Valid { + return nil, nil + } + return n.Time, nil +} diff --git a/vendor/github.com/SAP/go-hdb/internal/bufio/bufio.go b/vendor/github.com/SAP/go-hdb/internal/bufio/bufio.go new file mode 100644 index 000000000000..ed1d28d6fdfa --- /dev/null +++ b/vendor/github.com/SAP/go-hdb/internal/bufio/bufio.go @@ -0,0 +1,329 @@ +/* +Copyright 2014 SAP SE + +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 bufio implements buffered I/O for database read and writes on basis of the standard Go bufio package. +package bufio + +import ( + "bufio" + "encoding/binary" + "io" + "math" + + "github.com/SAP/go-hdb/internal/unicode" + "golang.org/x/text/transform" +) + +const ( + bufferSize = 128 +) + +// Reader is a bufio.Reader extended by methods needed for hdb protocol. +type Reader struct { + *bufio.Reader + b []byte // scratch buffer (min 8 Bytes) + tr transform.Transformer +} + +// NewReader creates a new Reader instance. +func NewReader(r io.Reader) *Reader { + return &Reader{ + Reader: bufio.NewReader(r), + b: make([]byte, bufferSize), + tr: unicode.Cesu8ToUtf8Transformer, + } +} + +// NewReaderSize creates a new Reader instance with given size for bufio.Reader. +func NewReaderSize(r io.Reader, size int) *Reader { + return &Reader{ + Reader: bufio.NewReaderSize(r, size), + b: make([]byte, bufferSize), + tr: unicode.Cesu8ToUtf8Transformer, + } +} + +// Skip skips cnt bytes from reading. +func (r *Reader) Skip(cnt int) error { + for i := 0; i < cnt; { + j := cnt - i + if j > len(r.b) { + j = len(r.b) + } + n, err := io.ReadFull(r.Reader, r.b[:j]) + i += n + if err != nil { + return err + } + } + return nil +} + +// ReadFull implements io.ReadFull on Reader. +func (r *Reader) ReadFull(p []byte) error { + _, err := io.ReadFull(r.Reader, p) + return err +} + +// ReadBool reads and returns a boolean. +func (r *Reader) ReadBool() (bool, error) { + c, err := r.Reader.ReadByte() + if err != nil { + return false, err + } + if c == 0 { + return false, nil + } + return true, nil +} + +// ReadInt8 reads and returns an int8. +func (r *Reader) ReadInt8() (int8, error) { + c, err := r.Reader.ReadByte() + if err != nil { + return 0, err + } + return int8(c), nil +} + +// ReadInt16 reads and returns an int16. +func (r *Reader) ReadInt16() (int16, error) { + if _, err := io.ReadFull(r.Reader, r.b[:2]); err != nil { + return 0, err + } + return int16(binary.LittleEndian.Uint16(r.b[:2])), nil +} + +// ReadUint16 reads and returns an uint16. +func (r *Reader) ReadUint16() (uint16, error) { + if _, err := io.ReadFull(r.Reader, r.b[:2]); err != nil { + return 0, err + } + return binary.LittleEndian.Uint16(r.b[:2]), nil +} + +// ReadInt32 reads and returns an int32. +func (r *Reader) ReadInt32() (int32, error) { + if _, err := io.ReadFull(r.Reader, r.b[:4]); err != nil { + return 0, err + } + return int32(binary.LittleEndian.Uint32(r.b[:4])), nil +} + +// ReadUint32 reads and returns an uint32. +func (r *Reader) ReadUint32() (uint32, error) { + if _, err := io.ReadFull(r.Reader, r.b[:4]); err != nil { + return 0, err + } + return binary.LittleEndian.Uint32(r.b[:4]), nil +} + +// ReadInt64 reads and returns an int64. +func (r *Reader) ReadInt64() (int64, error) { + if _, err := io.ReadFull(r.Reader, r.b[:8]); err != nil { + return 0, err + } + return int64(binary.LittleEndian.Uint64(r.b[:8])), nil +} + +// ReadUint64 reads and returns an uint64. +func (r *Reader) ReadUint64() (uint64, error) { + if _, err := io.ReadFull(r.Reader, r.b[:8]); err != nil { + return 0, err + } + return binary.LittleEndian.Uint64(r.b[:8]), nil +} + +// ReadFloat32 reads and returns a float32. +func (r *Reader) ReadFloat32() (float32, error) { + if _, err := io.ReadFull(r.Reader, r.b[:4]); err != nil { + return 0, err + } + bits := binary.LittleEndian.Uint32(r.b[:4]) + return math.Float32frombits(bits), nil +} + +// ReadFloat64 reads and returns a float64. +func (r *Reader) ReadFloat64() (float64, error) { + if _, err := io.ReadFull(r.Reader, r.b[:8]); err != nil { + return 0, err + } + bits := binary.LittleEndian.Uint64(r.b[:8]) + return math.Float64frombits(bits), nil +} + +// ReadCesu8 reads a size CESU-8 encoded byte sequence and returns an UTF-8 byte slice. +func (r *Reader) ReadCesu8(size int) ([]byte, error) { + p := make([]byte, size) + if _, err := io.ReadFull(r.Reader, p); err != nil { + return nil, err + } + r.tr.Reset() + n, _, err := r.tr.Transform(p, p, true) // inplace transformation + if err != nil { + return nil, err + } + return p[:n], nil +} + +// Writer is a bufio.Writer extended by methods needed for hdb protocol. +type Writer struct { + *bufio.Writer + b []byte // // scratch buffer (min 8 Bytes) + tr transform.Transformer +} + +// NewWriter creates a new Writer instance. +func NewWriter(w io.Writer) *Writer { + return &Writer{ + Writer: bufio.NewWriter(w), + b: make([]byte, bufferSize), + tr: unicode.Utf8ToCesu8Transformer, + } +} + +// NewWriterSize creates a new Writer instance with given size for bufio.Writer. +func NewWriterSize(w io.Writer, size int) *Writer { + return &Writer{ + Writer: bufio.NewWriterSize(w, size), + b: make([]byte, bufferSize), + tr: unicode.Utf8ToCesu8Transformer, + } +} + +// WriteZeroes writes cnt zero byte values. +func (w *Writer) WriteZeroes(cnt int) error { + // zero out scratch area + l := cnt + if l > len(w.b) { + l = len(w.b) + } + for i := 0; i < l; i++ { + w.b[i] = 0 + } + + for i := 0; i < cnt; { + j := cnt - i + if j > len(w.b) { + j = len(w.b) + } + n, err := w.Writer.Write(w.b[:j]) + i += n + if err != nil { + return err + } + } + return nil +} + +// WriteBool writes a boolean. +func (w *Writer) WriteBool(v bool) error { + if v { + return w.Writer.WriteByte(1) + } + return w.Writer.WriteByte(0) +} + +// WriteInt8 writes an int8. +func (w *Writer) WriteInt8(i int8) error { + return w.Writer.WriteByte(byte(i)) +} + +// WriteInt16 writes an int16. +func (w *Writer) WriteInt16(i int16) error { + binary.LittleEndian.PutUint16(w.b[:2], uint16(i)) + _, err := w.Writer.Write(w.b[:2]) + return err +} + +// WriteUint16 writes an uint16. +func (w *Writer) WriteUint16(i uint16) error { + binary.LittleEndian.PutUint16(w.b[:2], i) + _, err := w.Writer.Write(w.b[:2]) + return err +} + +// WriteInt32 writes an int32. +func (w *Writer) WriteInt32(i int32) error { + binary.LittleEndian.PutUint32(w.b[:4], uint32(i)) + _, err := w.Writer.Write(w.b[:4]) + return err +} + +// WriteUint32 writes an uint32. +func (w *Writer) WriteUint32(i uint32) error { + binary.LittleEndian.PutUint32(w.b[:4], i) + _, err := w.Writer.Write(w.b[:4]) + return err +} + +// WriteInt64 writes an int64. +func (w *Writer) WriteInt64(i int64) error { + binary.LittleEndian.PutUint64(w.b[:8], uint64(i)) + _, err := w.Writer.Write(w.b[:8]) + return err +} + +// WriteUint64 writes an uint64. +func (w *Writer) WriteUint64(i uint64) error { + binary.LittleEndian.PutUint64(w.b[:8], i) + _, err := w.Writer.Write(w.b[:8]) + return err +} + +// WriteFloat32 writes a float32. +func (w *Writer) WriteFloat32(f float32) error { + bits := math.Float32bits(f) + binary.LittleEndian.PutUint32(w.b[:4], bits) + _, err := w.Writer.Write(w.b[:4]) + return err +} + +// WriteFloat64 writes a float64. +func (w *Writer) WriteFloat64(f float64) error { + bits := math.Float64bits(f) + binary.LittleEndian.PutUint64(w.b[:8], bits) + _, err := w.Writer.Write(w.b[:8]) + return err +} + +// WriteCesu8 writes an UTF-8 byte slice as CESU-8 and returns the CESU-8 bytes written. +func (w *Writer) WriteCesu8(p []byte) (int, error) { + w.tr.Reset() + cnt := 0 + i := 0 + for i < len(p) { + m, n, err := w.tr.Transform(w.b, p[i:], true) + if err != nil && err != transform.ErrShortDst { + return cnt, err + } + if m == 0 { + return cnt, transform.ErrShortDst + } + o, err := w.Writer.Write(w.b[:m]) + cnt += o + if err != nil { + return cnt, err + } + i += n + } + return cnt, nil +} + +// WriteStringCesu8 is like WriteCesu8 with an UTF-8 string as parameter. +func (w *Writer) WriteStringCesu8(s string) (int, error) { + return w.WriteCesu8([]byte(s)) +} diff --git a/vendor/github.com/SAP/go-hdb/internal/protocol/clientid.go b/vendor/github.com/SAP/go-hdb/internal/protocol/clientid.go new file mode 100644 index 000000000000..a24270aeb12d --- /dev/null +++ b/vendor/github.com/SAP/go-hdb/internal/protocol/clientid.go @@ -0,0 +1,56 @@ +/* +Copyright 2014 SAP SE + +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 protocol + +import ( + "os" + "strconv" + "strings" + + "github.com/SAP/go-hdb/internal/bufio" +) + +type clientID []byte + +func newClientID() clientID { + if h, err := os.Hostname(); err == nil { + return clientID(strings.Join([]string{strconv.Itoa(os.Getpid()), h}, "@")) + } + return clientID(strconv.Itoa(os.Getpid())) +} + +func (id clientID) kind() partKind { + return partKind(35) //TODO: extend part kind +} + +func (id clientID) size() (int, error) { + return len(id), nil +} + +func (id clientID) numArg() int { + return 1 +} + +func (id clientID) write(wr *bufio.Writer) error { + if _, err := wr.Write(id); err != nil { + return err + } + if trace { + outLogger.Printf("client id: %s", id) + } + return nil +} diff --git a/vendor/github.com/SAP/go-hdb/internal/protocol/command.go b/vendor/github.com/SAP/go-hdb/internal/protocol/command.go new file mode 100644 index 000000000000..041e154cdfca --- /dev/null +++ b/vendor/github.com/SAP/go-hdb/internal/protocol/command.go @@ -0,0 +1,49 @@ +/* +Copyright 2014 SAP SE + +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 protocol + +import ( + "github.com/SAP/go-hdb/internal/bufio" + "github.com/SAP/go-hdb/internal/unicode/cesu8" +) + +// cesu8 command +type command []byte + +func (c command) kind() partKind { + return pkCommand +} + +func (c command) size() (int, error) { + return cesu8.Size(c), nil +} + +func (c command) numArg() int { + return 1 +} + +func (c command) write(wr *bufio.Writer) error { + + if _, err := wr.WriteCesu8(c); err != nil { + return err + } + if trace { + outLogger.Printf("command: %s", c) + } + + return nil +} diff --git a/vendor/github.com/SAP/go-hdb/internal/protocol/connectoption.go b/vendor/github.com/SAP/go-hdb/internal/protocol/connectoption.go new file mode 100644 index 000000000000..00a9e4254930 --- /dev/null +++ b/vendor/github.com/SAP/go-hdb/internal/protocol/connectoption.go @@ -0,0 +1,49 @@ +/* +Copyright 2014 SAP SE + +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 protocol + +//go:generate stringer -type=connectOption + +type connectOption int8 + +const ( + coConnectionID connectOption = 1 + coCompleteArrayExecution connectOption = 2 + coClientLocale connectOption = 3 + coSupportsLargeBulkOperations connectOption = 4 + // docu: error field mentioned twice + //coDataFormatVersion2 connectOption = 5 + coLargeNumberOfParameterSupport connectOption = 10 + coSystemID connectOption = 11 + // missing in docu + coDataFormatVersion connectOption = 12 + coAbapVarcharMode connectOption = 13 + coSelectForUpdateSupported connectOption = 14 + coClientDistributionMode connectOption = 15 + coEngineDataFormatVersion connectOption = 16 + coDistributionProtocolVersion connectOption = 17 + coSplitBatchCommands connectOption = 18 + coUseTransactionFlagsOnly connectOption = 19 + coRowAndColumnOptimizedFormat connectOption = 20 + coIgnoreUnknownParts connectOption = 21 + coTableOutputParameter connectOption = 22 + coDataFormatVersion2 connectOption = 23 + coItabParameter connectOption = 24 + coDescribeTableOutputParameter connectOption = 25 + coScrollablResultSet connectOption = 27 + // docu? connectOption = 28 //boolean +) diff --git a/vendor/github.com/SAP/go-hdb/internal/protocol/connectoption_string.go b/vendor/github.com/SAP/go-hdb/internal/protocol/connectoption_string.go new file mode 100644 index 000000000000..f49c5beeae43 --- /dev/null +++ b/vendor/github.com/SAP/go-hdb/internal/protocol/connectoption_string.go @@ -0,0 +1,32 @@ +// generated by stringer -type=connectOption; DO NOT EDIT + +package protocol + +import "fmt" + +const ( + _connectOption_name_0 = "coConnectionIDcoCompleteArrayExecutioncoClientLocalecoSupportsLargeBulkOperations" + _connectOption_name_1 = "coLargeNumberOfParameterSupportcoSystemIDcoDataFormatVersioncoAbapVarcharModecoSelectForUpdateSupportedcoClientDistributionModecoEngineDataFormatVersioncoDistributionProtocolVersioncoSplitBatchCommandscoUseTransactionFlagsOnlycoRowAndColumnOptimizedFormatcoIgnoreUnknownPartscoTableOutputParametercoDataFormatVersion2coItabParametercoDescribeTableOutputParameter" + _connectOption_name_2 = "coScrollablResultSet" +) + +var ( + _connectOption_index_0 = [...]uint8{0, 14, 38, 52, 81} + _connectOption_index_1 = [...]uint16{0, 31, 41, 60, 77, 103, 127, 152, 181, 201, 226, 255, 275, 297, 317, 332, 362} + _connectOption_index_2 = [...]uint8{0, 20} +) + +func (i connectOption) String() string { + switch { + case 1 <= i && i <= 4: + i -= 1 + return _connectOption_name_0[_connectOption_index_0[i]:_connectOption_index_0[i+1]] + case 10 <= i && i <= 25: + i -= 10 + return _connectOption_name_1[_connectOption_index_1[i]:_connectOption_index_1[i+1]] + case i == 27: + return _connectOption_name_2 + default: + return fmt.Sprintf("connectOption(%d)", i) + } +} diff --git a/vendor/github.com/SAP/go-hdb/internal/protocol/connectoptions.go b/vendor/github.com/SAP/go-hdb/internal/protocol/connectoptions.go new file mode 100644 index 000000000000..04686e340953 --- /dev/null +++ b/vendor/github.com/SAP/go-hdb/internal/protocol/connectoptions.go @@ -0,0 +1,113 @@ +/* +Copyright 2014 SAP SE + +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 protocol + +import ( + "fmt" + + "github.com/SAP/go-hdb/internal/bufio" +) + +// data format version +const ( + dfvBaseline intType = 1 + dfvDoNotUse = 3 + dfvSPS06 = 4 //see docu + dfvBINTEXT = 6 +) + +// client distribution mode +const ( + cdmOff intType = 0 + cdmConnection = 1 + cdmStatement = 2 + cdmConnectionStatement = 3 +) + +// distribution protocol version +const ( + dpvBaseline = 0 + dpvClientHandlesStatementSequence = 1 +) + +type connectOptions struct { + po plainOptions + _numArg int +} + +func newConnectOptions() *connectOptions { + return &connectOptions{ + po: plainOptions{}, + } +} + +func (o *connectOptions) String() string { + m := make(map[connectOption]interface{}) + for k, v := range o.po { + m[connectOption(k)] = v + } + return fmt.Sprintf("%s", m) +} + +func (o *connectOptions) kind() partKind { + return pkConnectOptions +} + +func (o *connectOptions) size() (int, error) { + return o.po.size(), nil +} + +func (o *connectOptions) numArg() int { + return len(o.po) +} + +func (o *connectOptions) setNumArg(numArg int) { + o._numArg = numArg +} + +func (o *connectOptions) set(k connectOption, v interface{}) { + o.po[int8(k)] = v +} + +func (o *connectOptions) get(k connectOption) (interface{}, bool) { + v, ok := o.po[int8(k)] + return v, ok +} + +func (o *connectOptions) read(rd *bufio.Reader) error { + + if err := o.po.read(rd, o._numArg); err != nil { + return err + } + if trace { + outLogger.Printf("connect options: %v", o) + } + + return nil +} + +func (o *connectOptions) write(wr *bufio.Writer) error { + + if err := o.po.write(wr); err != nil { + return err + } + if trace { + outLogger.Printf("connect options: %v", o) + } + + return nil +} diff --git a/vendor/github.com/SAP/go-hdb/internal/protocol/datatype.go b/vendor/github.com/SAP/go-hdb/internal/protocol/datatype.go new file mode 100644 index 000000000000..257035f12432 --- /dev/null +++ b/vendor/github.com/SAP/go-hdb/internal/protocol/datatype.go @@ -0,0 +1,38 @@ +/* +Copyright 2014 SAP SE + +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 protocol + +//go:generate stringer -type=DataType + +// DataType is the type definition for data types supported by this package. +type DataType byte + +// Data type constants. +const ( + DtUnknown DataType = iota // unknown data type + DtTinyint + DtSmallint + DtInt + DtBigint + DtReal + DtDouble + DtDecimal + DtTime + DtString + DtBytes + DtLob +) diff --git a/vendor/github.com/SAP/go-hdb/internal/protocol/datatype_string.go b/vendor/github.com/SAP/go-hdb/internal/protocol/datatype_string.go new file mode 100644 index 000000000000..148f4a5e0dce --- /dev/null +++ b/vendor/github.com/SAP/go-hdb/internal/protocol/datatype_string.go @@ -0,0 +1,16 @@ +// generated by stringer -type=DataType; DO NOT EDIT + +package protocol + +import "fmt" + +const _DataType_name = "DtUnknownDtTinyintDtSmallintDtIntDtBigintDtRealDtDoubleDtDecimalDtTimeDtVarcharDtNvarcharDtLob" + +var _DataType_index = [...]uint8{0, 9, 18, 28, 33, 41, 47, 55, 64, 70, 79, 89, 94} + +func (i DataType) String() string { + if i >= DataType(len(_DataType_index)-1) { + return fmt.Sprintf("DataType(%d)", i) + } + return _DataType_name[_DataType_index[i]:_DataType_index[i+1]] +} diff --git a/vendor/github.com/SAP/go-hdb/internal/protocol/doc.go b/vendor/github.com/SAP/go-hdb/internal/protocol/doc.go new file mode 100644 index 000000000000..f23fe24919f2 --- /dev/null +++ b/vendor/github.com/SAP/go-hdb/internal/protocol/doc.go @@ -0,0 +1,20 @@ +/* +Copyright 2014 SAP SE + +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 protocol implements the hdb command network protocol. +// +// http://help.sap.com/hana/SAP_HANA_SQL_Command_Network_Protocol_Reference_en.pdf +package protocol diff --git a/vendor/github.com/SAP/go-hdb/internal/protocol/endianess.go b/vendor/github.com/SAP/go-hdb/internal/protocol/endianess.go new file mode 100644 index 000000000000..579c2a7e284c --- /dev/null +++ b/vendor/github.com/SAP/go-hdb/internal/protocol/endianess.go @@ -0,0 +1,26 @@ +/* +Copyright 2014 SAP SE + +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 protocol + +//go:generate stringer -type=endianess + +type endianess int8 + +const ( + bigEndian endianess = 0 + littleEndian endianess = 1 +) diff --git a/vendor/github.com/SAP/go-hdb/internal/protocol/endianess_string.go b/vendor/github.com/SAP/go-hdb/internal/protocol/endianess_string.go new file mode 100644 index 000000000000..66174f4b5d27 --- /dev/null +++ b/vendor/github.com/SAP/go-hdb/internal/protocol/endianess_string.go @@ -0,0 +1,16 @@ +// generated by stringer -type=endianess; DO NOT EDIT + +package protocol + +import "fmt" + +const _endianess_name = "bigEndianlittleEndian" + +var _endianess_index = [...]uint8{0, 9, 21} + +func (i endianess) String() string { + if i < 0 || i >= endianess(len(_endianess_index)-1) { + return fmt.Sprintf("endianess(%d)", i) + } + return _endianess_name[_endianess_index[i]:_endianess_index[i+1]] +} diff --git a/vendor/github.com/SAP/go-hdb/internal/protocol/error.go b/vendor/github.com/SAP/go-hdb/internal/protocol/error.go new file mode 100644 index 000000000000..3c450c5664c7 --- /dev/null +++ b/vendor/github.com/SAP/go-hdb/internal/protocol/error.go @@ -0,0 +1,146 @@ +/* +Copyright 2014 SAP SE + +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 protocol + +import ( + "fmt" + + "github.com/SAP/go-hdb/internal/bufio" +) + +const ( + sqlStateSize = 5 +) + +type sqlState [sqlStateSize]byte + +type hdbError struct { + errorCode int32 + errorPosition int32 + errorTextLength int32 + errorLevel ErrorLevel + sqlState sqlState + errorText []byte +} + +func newHdbError() *hdbError { + return &hdbError{} +} + +// String implements the Stringer interface. +func (e *hdbError) String() string { + return fmt.Sprintf("errorCode %d, errorPosition %d, errorTextLength % d errorLevel %s, sqlState %s errorText %s", + e.errorCode, + e.errorPosition, + e.errorTextLength, + e.errorLevel, + e.sqlState, + e.errorText, + ) +} + +// Error implements the Error interface. +func (e *hdbError) Error() string { + return fmt.Sprintf("SQL %s %d - %s", e.errorLevel, e.errorCode, e.errorText) +} + +// Code implements the hdb.Error interface. +func (e *hdbError) Code() int { + return int(e.errorCode) +} + +// Position implements the hdb.Error interface. +func (e *hdbError) Position() int { + return int(e.errorPosition) +} + +// Level implements the hdb.Error interface. +func (e *hdbError) Level() ErrorLevel { + return e.errorLevel +} + +// Text implements the hdb.Error interface. +func (e *hdbError) Text() string { + return string(e.errorText) +} + +// IsWarning implements the hdb.Error interface. +func (e *hdbError) IsWarning() bool { + return e.errorLevel == HdbWarning +} + +// IsError implements the hdb.Error interface. +func (e *hdbError) IsError() bool { + return e.errorLevel == HdbError +} + +// IsFatal implements the hdb.Error interface. +func (e *hdbError) IsFatal() bool { + return e.errorLevel == HdbFatalError +} + +func (e *hdbError) kind() partKind { + return pkError +} + +func (e *hdbError) setNumArg(int) { + // not needed +} + +func (e *hdbError) read(rd *bufio.Reader) error { + var err error + + if e.errorCode, err = rd.ReadInt32(); err != nil { + return err + } + if e.errorPosition, err = rd.ReadInt32(); err != nil { + return err + } + if e.errorTextLength, err = rd.ReadInt32(); err != nil { + return err + } + + el, err := rd.ReadInt8() + if err != nil { + return err + } + e.errorLevel = ErrorLevel(el) + + if err := rd.ReadFull(e.sqlState[:]); err != nil { + return err + } + + // read error text as ASCII data as some errors return invalid CESU-8 characters + // e.g: SQL HdbError 7 - feature not supported: invalid character encoding: + // if e.errorText, err = rd.ReadCesu8(int(e.errorTextLength)); err != nil { + // return err + // } + e.errorText = make([]byte, int(e.errorTextLength)) + if _, err = rd.Read(e.errorText); err != nil { + return err + } + + // part bufferlength is by one greater than real error length? --> read filler byte + if _, err := rd.ReadByte(); err != nil { + return err + } + if trace { + outLogger.Printf("error: %s", e) + } + + return nil +} diff --git a/vendor/github.com/SAP/go-hdb/internal/protocol/errorlevel.go b/vendor/github.com/SAP/go-hdb/internal/protocol/errorlevel.go new file mode 100644 index 000000000000..af8b26dfc5e1 --- /dev/null +++ b/vendor/github.com/SAP/go-hdb/internal/protocol/errorlevel.go @@ -0,0 +1,29 @@ +/* +Copyright 2014 SAP SE + +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 protocol + +//go:generate stringer -type=ErrorLevel + +// ErrorLevel send from database server. +type ErrorLevel int8 + +// HDB error level constants. +const ( + HdbWarning ErrorLevel = 0 + HdbError ErrorLevel = 1 + HdbFatalError ErrorLevel = 2 +) diff --git a/vendor/github.com/SAP/go-hdb/internal/protocol/errorlevel_string.go b/vendor/github.com/SAP/go-hdb/internal/protocol/errorlevel_string.go new file mode 100644 index 000000000000..723c0e1d45a6 --- /dev/null +++ b/vendor/github.com/SAP/go-hdb/internal/protocol/errorlevel_string.go @@ -0,0 +1,16 @@ +// generated by stringer -type=ErrorLevel; DO NOT EDIT + +package protocol + +import "fmt" + +const _ErrorLevel_name = "HdbWarningHdbErrorHdbFatalError" + +var _ErrorLevel_index = [...]uint8{0, 10, 18, 31} + +func (i ErrorLevel) String() string { + if i < 0 || i >= ErrorLevel(len(_ErrorLevel_index)-1) { + return fmt.Sprintf("ErrorLevel(%d)", i) + } + return _ErrorLevel_name[_ErrorLevel_index[i]:_ErrorLevel_index[i+1]] +} diff --git a/vendor/github.com/SAP/go-hdb/internal/protocol/fetchsize.go b/vendor/github.com/SAP/go-hdb/internal/protocol/fetchsize.go new file mode 100644 index 000000000000..fb62a8edadfe --- /dev/null +++ b/vendor/github.com/SAP/go-hdb/internal/protocol/fetchsize.go @@ -0,0 +1,48 @@ +/* +Copyright 2014 SAP SE + +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 protocol + +import ( + "github.com/SAP/go-hdb/internal/bufio" +) + +//fetch size +type fetchsize int32 + +func (s fetchsize) kind() partKind { + return pkFetchSize +} + +func (s fetchsize) size() (int, error) { + return 4, nil +} + +func (s fetchsize) numArg() int { + return 1 +} + +func (s fetchsize) write(wr *bufio.Writer) error { + + if err := wr.WriteInt32(int32(s)); err != nil { + return err + } + if trace { + outLogger.Printf("fetchsize: %d", s) + } + + return nil +} diff --git a/vendor/github.com/SAP/go-hdb/internal/protocol/field.go b/vendor/github.com/SAP/go-hdb/internal/protocol/field.go new file mode 100644 index 000000000000..46d121fb01c1 --- /dev/null +++ b/vendor/github.com/SAP/go-hdb/internal/protocol/field.go @@ -0,0 +1,909 @@ +/* +Copyright 2014 SAP SE + +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 protocol + +import ( + "database/sql/driver" + "fmt" + "math" + "sort" + "time" + + "github.com/SAP/go-hdb/internal/bufio" + "github.com/SAP/go-hdb/internal/unicode/cesu8" +) + +const ( + realNullValue uint32 = ^uint32(0) + doubleNullValue uint64 = ^uint64(0) +) + +type uint32Slice []uint32 + +func (p uint32Slice) Len() int { return len(p) } +func (p uint32Slice) Less(i, j int) bool { return p[i] < p[j] } +func (p uint32Slice) Swap(i, j int) { p[i], p[j] = p[j], p[i] } +func (p uint32Slice) sort() { sort.Sort(p) } + +type field interface { + typeCode() typeCode + in() bool + out() bool + name(map[uint32]string) string + nameOffsets() []uint32 + String() string +} + +// FieldSet contains database field metadata. +type FieldSet struct { + fields []field + names map[uint32]string +} + +func newFieldSet(size int) *FieldSet { + return &FieldSet{ + fields: make([]field, size), + names: make(map[uint32]string), + } +} + +// String implements the Stringer interface. +func (f *FieldSet) String() string { + a := make([]string, len(f.fields)) + for i, f := range f.fields { + a[i] = f.String() + } + return fmt.Sprintf("%v", a) +} + +func (f *FieldSet) nameOffsets() []uint32 { + for _, field := range f.fields { + for _, offset := range field.nameOffsets() { + if offset != 0xFFFFFFFF { + f.names[offset] = "" + } + } + } + // sort offsets (not sure if offsets are monotonically increasing in any case) + offsets := make([]uint32, len(f.names)) + i := 0 + for offset := range f.names { + offsets[i] = offset + i++ + } + uint32Slice(offsets).sort() + return offsets +} + +// NumInputField returns the number of input fields in a database statement. +func (f *FieldSet) NumInputField() int { + cnt := 0 + for _, field := range f.fields { + if field.in() { + cnt++ + } + } + return cnt +} + +// NumOutputField returns the number of output fields of a query or stored procedure. +func (f *FieldSet) NumOutputField() int { + cnt := 0 + for _, field := range f.fields { + if field.out() { + cnt++ + } + } + return cnt +} + +// DataType returns the datatype of the field at index idx. +func (f *FieldSet) DataType(idx int) DataType { + return f.fields[idx].typeCode().dataType() +} + +// OutputNames fills the names parameter with field names of all output fields. The size of the names slice must be at least +// NumOutputField big. +func (f *FieldSet) OutputNames(names []string) error { + i := 0 + for _, field := range f.fields { + if field.out() { + if i >= len(names) { // assert names size + return fmt.Errorf("names size too short %d - expected min %d", len(names), i) + } + names[i] = field.name(f.names) + i++ + } + } + return nil +} + +// FieldValues contains rows read from database. +type FieldValues struct { + s *Session + + rows int + cols int + lobCols int + values []driver.Value + + descrs []*LobReadDescr // Caution: store descriptor to guarantee valid addresses + writers []lobWriter +} + +func newFieldValues(s *Session) *FieldValues { + return &FieldValues{s: s} +} + +func (f *FieldValues) String() string { + return fmt.Sprintf("rows %d columns %d lob columns %d", f.rows, f.cols, f.lobCols) +} + +func (f *FieldValues) read(rows int, fieldSet *FieldSet, rd *bufio.Reader) error { + f.rows = rows + f.descrs = make([]*LobReadDescr, 0) + + f.cols, f.lobCols = 0, 0 + for _, field := range fieldSet.fields { + if field.out() { + if field.typeCode().isLob() { + f.descrs = append(f.descrs, &LobReadDescr{col: f.cols}) + f.lobCols++ + } + f.cols++ + } + } + f.values = make([]driver.Value, f.rows*f.cols) + f.writers = make([]lobWriter, f.lobCols) + + for i := 0; i < f.rows; i++ { + j := 0 + for _, field := range fieldSet.fields { + + if !field.out() { + continue + } + + var err error + if f.values[i*f.cols+j], err = readField(rd, field.typeCode()); err != nil { + return err + } + j++ + } + } + return nil +} + +// NumRow returns the number of rows available in FieldValues. +func (f *FieldValues) NumRow() int { + return f.rows +} + +// Row fills the dest value slice with row data at index idx. +func (f *FieldValues) Row(idx int, dest []driver.Value) { + copy(dest, f.values[idx*f.cols:(idx+1)*f.cols]) + + if f.lobCols == 0 { + return + } + + for i, descr := range f.descrs { + col := descr.col + writer := dest[col].(lobWriter) + f.writers[i] = writer + descr.w = writer + dest[col] = lobReadDescrToPointer(descr) + } + + // last descriptor triggers lob read + f.descrs[f.lobCols-1].fn = func() error { + return f.s.readLobStream(f.writers) + } +} + +const ( + tinyintFieldSize = 1 + smallintFieldSize = 2 + intFieldSize = 4 + bigintFieldSize = 8 + realFieldSize = 4 + doubleFieldSize = 8 + dateFieldSize = 4 + timeFieldSize = 4 + timestampFieldSize = dateFieldSize + timeFieldSize + decimalFieldSize = 16 + lobInputDescriptorSize = 9 +) + +func fieldSize(tc typeCode, v driver.Value) (int, error) { + + if v == nil { + return 0, nil + } + + switch tc { + case tcTinyint: + return tinyintFieldSize, nil + case tcSmallint: + return smallintFieldSize, nil + case tcInt: + return intFieldSize, nil + case tcBigint: + return bigintFieldSize, nil + case tcReal: + return realFieldSize, nil + case tcDouble: + return doubleFieldSize, nil + case tcDate: + return dateFieldSize, nil + case tcTime: + return timeFieldSize, nil + case tcTimestamp: + return timestampFieldSize, nil + case tcDecimal: + return decimalFieldSize, nil + case tcChar, tcVarchar, tcString: + switch v := v.(type) { + case []byte: + return bytesSize(len(v)) + case string: + return bytesSize(len(v)) + default: + outLogger.Fatalf("data type %s mismatch %T", tc, v) + } + case tcNchar, tcNvarchar, tcNstring: + switch v := v.(type) { + case []byte: + return bytesSize(cesu8.Size(v)) + case string: + return bytesSize(cesu8.StringSize(v)) + default: + outLogger.Fatalf("data type %s mismatch %T", tc, v) + } + case tcBinary, tcVarbinary: + v, ok := v.([]byte) + if !ok { + outLogger.Fatalf("data type %s mismatch %T", tc, v) + } + return bytesSize(len(v)) + case tcBlob, tcClob, tcNclob: + return lobInputDescriptorSize, nil + } + outLogger.Fatalf("data type %s not implemented", tc) + return 0, nil +} + +func readField(rd *bufio.Reader, tc typeCode) (interface{}, error) { + + switch tc { + + case tcTinyint, tcSmallint, tcInt, tcBigint: + + valid, err := rd.ReadBool() + if err != nil { + return nil, err + } + if !valid { //null value + return nil, nil + } + + switch tc { + + case tcTinyint: + if v, err := rd.ReadByte(); err == nil { + return int64(v), nil + } + return nil, err + + case tcSmallint: + if v, err := rd.ReadInt16(); err == nil { + return int64(v), nil + } + return nil, err + + case tcInt: + if v, err := rd.ReadInt32(); err == nil { + return int64(v), nil + } + return nil, err + + case tcBigint: + if v, err := rd.ReadInt64(); err == nil { + return v, nil + } + return nil, err + } + + case tcReal: + v, err := rd.ReadUint32() + if err != nil { + return nil, err + } + if v == realNullValue { + return nil, nil + } + return float64(math.Float32frombits(v)), nil + + case tcDouble: + v, err := rd.ReadUint64() + if err != nil { + return nil, err + } + if v == doubleNullValue { + return nil, nil + } + return math.Float64frombits(v), nil + + case tcDate: + + year, month, day, null, err := readDate(rd) + if err != nil { + return nil, err + } + if null { + return nil, nil + } + + return time.Date(year, month, day, 0, 0, 0, 0, time.UTC), nil + + // time read gives only seconds (cut), no milliseconds + case tcTime: + + hour, minute, nanosecs, null, err := readTime(rd) + if err != nil { + return nil, err + } + if null { + return nil, nil + } + + return time.Date(1, 1, 1, hour, minute, 0, nanosecs, time.UTC), nil + + case tcTimestamp: + + year, month, day, dateNull, err := readDate(rd) + if err != nil { + return nil, err + } + + hour, minute, nanosecs, timeNull, err := readTime(rd) + if err != nil { + return nil, err + } + + if dateNull || timeNull { + return nil, nil + } + + return time.Date(year, month, day, hour, minute, 0, nanosecs, time.UTC), nil + + case tcDecimal: + + b, null, err := readDecimal(rd) + switch { + case err != nil: + return nil, err + case null: + return nil, nil + default: + return b, nil + } + + case tcChar, tcVarchar: + value, null, err := readBytes(rd) + if err != nil { + return nil, err + } + if null { + return nil, nil + } + return value, nil + + case tcNchar, tcNvarchar: + value, null, err := readUtf8(rd) + if err != nil { + return nil, err + } + if null { + return nil, nil + } + return value, nil + + case tcBinary, tcVarbinary: + value, null, err := readBytes(rd) + if err != nil { + return nil, err + } + if null { + return nil, nil + } + return value, nil + + case tcBlob, tcClob, tcNclob: + null, writer, err := readLob(rd, tc) + if err != nil { + return nil, err + } + if null { + return nil, nil + } + return writer, nil + } + + outLogger.Fatalf("read field: type code %s not implemented", tc) + return nil, nil +} + +func writeField(wr *bufio.Writer, tc typeCode, v driver.Value) error { + + // null value + if v == nil { + if err := wr.WriteByte(byte(tc) | 0x80); err != nil { //set high bit + return err + } + return nil + } + + // type code + if err := wr.WriteByte(byte(tc)); err != nil { + return err + } + + switch tc { + + // TODO: char, ... + + case tcTinyint, tcSmallint, tcInt, tcBigint: + + i64, ok := v.(int64) + if !ok { + return fmt.Errorf("invalid argument type %T", v) + } + + switch tc { + case tcTinyint: + return wr.WriteByte(byte(i64)) + case tcSmallint: + return wr.WriteInt16(int16(i64)) + case tcInt: + return wr.WriteInt32(int32(i64)) + case tcBigint: + return wr.WriteInt64(i64) + } + + case tcReal: + + f64, ok := v.(float64) + if !ok { + return fmt.Errorf("invalid argument type %T", v) + } + return wr.WriteFloat32(float32(f64)) + + case tcDouble: + + f64, ok := v.(float64) + if !ok { + return fmt.Errorf("invalid argument type %T", v) + } + return wr.WriteFloat64(f64) + + case tcDate: + t, ok := v.(time.Time) + if !ok { + return fmt.Errorf("invalid argument type %T", v) + } + return writeDate(wr, t) + + case tcTime: + t, ok := v.(time.Time) + if !ok { + return fmt.Errorf("invalid argument type %T", v) + } + return writeTime(wr, t) + + case tcTimestamp: + t, ok := v.(time.Time) + if !ok { + return fmt.Errorf("invalid argument type %T", v) + } + if err := writeDate(wr, t); err != nil { + return err + } + return writeTime(wr, t) + + case tcDecimal: + b, ok := v.([]byte) + if !ok { + return fmt.Errorf("invalid argument type %T", v) + } + if len(b) != 16 { + return fmt.Errorf("invalid argument length %d of type %T - expected %d", len(b), v, 16) + } + _, err := wr.Write(b) + return err + + case tcChar, tcVarchar, tcString: + switch v := v.(type) { + case []byte: + return writeBytes(wr, v) + case string: + return writeString(wr, v) + default: + return fmt.Errorf("invalid argument type %T", v) + } + + case tcNchar, tcNvarchar, tcNstring: + switch v := v.(type) { + case []byte: + return writeUtf8Bytes(wr, v) + case string: + return writeUtf8String(wr, v) + default: + return fmt.Errorf("invalid argument type %T", v) + } + + case tcBinary, tcVarbinary: + v, ok := v.([]byte) + if !ok { + return fmt.Errorf("invalid argument type %T", v) + } + return writeBytes(wr, v) + + case tcBlob, tcClob, tcNclob: + return writeLob(wr) + } + + outLogger.Fatalf("write field: type code %s not implemented", tc) + return nil +} + +// null values: most sig bit unset +// year: unset second most sig bit (subtract 2^15) +// --> read year as unsigned +// month is 0-based +// day is 1 byte +func readDate(rd *bufio.Reader) (int, time.Month, int, bool, error) { + + year, err := rd.ReadUint16() + if err != nil { + return 0, 0, 0, false, err + } + if (year & 0x8000) == 0 { //null value + if err := rd.Skip(2); err != nil { + return 0, 0, 0, false, err + } + return 0, 0, 0, true, nil + } + year &= 0x3fff + month, err := rd.ReadInt8() + if err != nil { + return 0, 0, 0, false, err + } + month++ + day, err := rd.ReadInt8() + if err != nil { + return 0, 0, 0, false, err + } + return int(year), time.Month(month), int(day), false, nil +} + +// year: set most sig bit +// month 0 based +func writeDate(wr *bufio.Writer, t time.Time) error { + + //store in utc + utc := t.In(time.UTC) + + year, month, day := utc.Date() + + if err := wr.WriteUint16(uint16(year) | 0x8000); err != nil { + return err + } + if err := wr.WriteInt8(int8(month) - 1); err != nil { + return err + } + if err := wr.WriteInt8(int8(day)); err != nil { + return err + } + return nil +} + +func readTime(rd *bufio.Reader) (int, int, int, bool, error) { + + hour, err := rd.ReadByte() + if err != nil { + return 0, 0, 0, false, err + } + if (hour & 0x80) == 0 { //null value + if err := rd.Skip(3); err != nil { + return 0, 0, 0, false, err + } + return 0, 0, 0, true, nil + } + hour &= 0x7f + minute, err := rd.ReadInt8() + if err != nil { + return 0, 0, 0, false, err + } + millisecs, err := rd.ReadUint16() + if err != nil { + return 0, 0, 0, false, err + } + + nanosecs := int(millisecs) * 1000000 + + return int(hour), int(minute), nanosecs, false, nil +} + +func writeTime(wr *bufio.Writer, t time.Time) error { + + //store in utc + utc := t.UTC() + + if err := wr.WriteByte(byte(utc.Hour()) | 0x80); err != nil { + return err + } + if err := wr.WriteInt8(int8(utc.Minute())); err != nil { + return err + } + + millisecs := utc.Second()*1000 + utc.Round(time.Millisecond).Nanosecond()/1000000 + + if err := wr.WriteUint16(uint16(millisecs)); err != nil { + return err + } + + return nil +} + +func readDecimal(rd *bufio.Reader) ([]byte, bool, error) { + b := make([]byte, 16) + if err := rd.ReadFull(b); err != nil { + return nil, false, err + } + if (b[15] & 0x70) == 0x70 { //null value (bit 4,5,6 set) + return nil, true, nil + } + return b, false, nil +} + +// string / binary length indicators +const ( + bytesLenIndNullValue byte = 255 + bytesLenIndSmall byte = 245 + bytesLenIndMedium byte = 246 + bytesLenIndBig byte = 247 +) + +func bytesSize(size int) (int, error) { //size + length indicator + switch { + default: + return 0, fmt.Errorf("max string length %d exceeded %d", math.MaxInt32, size) + case size <= int(bytesLenIndSmall): + return size + 1, nil + case size <= math.MaxInt16: + return size + 3, nil + case size <= math.MaxInt32: + return size + 5, nil + } +} + +func readBytesSize(rd *bufio.Reader) (int, bool, error) { + + ind, err := rd.ReadByte() //length indicator + if err != nil { + return 0, false, err + } + + switch { + + default: + return 0, false, fmt.Errorf("invalid length indicator %d", ind) + + case ind == bytesLenIndNullValue: + return 0, true, nil + + case ind <= bytesLenIndSmall: + return int(ind), false, nil + + case ind == bytesLenIndMedium: + if size, err := rd.ReadInt16(); err == nil { + return int(size), false, nil + } + return 0, false, err + + case ind == bytesLenIndBig: + if size, err := rd.ReadInt32(); err == nil { + return int(size), false, nil + } + return 0, false, err + } +} + +func writeBytesSize(wr *bufio.Writer, size int) error { + switch { + + default: + return fmt.Errorf("max argument length %d of string exceeded", size) + + case size <= int(bytesLenIndSmall): + if err := wr.WriteByte(byte(size)); err != nil { + return err + } + case size <= math.MaxInt16: + if err := wr.WriteByte(bytesLenIndMedium); err != nil { + return err + } + if err := wr.WriteInt16(int16(size)); err != nil { + return err + } + case size <= math.MaxInt32: + if err := wr.WriteByte(bytesLenIndBig); err != nil { + return err + } + if err := wr.WriteInt32(int32(size)); err != nil { + return err + } + } + return nil +} + +func readBytes(rd *bufio.Reader) ([]byte, bool, error) { + size, null, err := readBytesSize(rd) + if err != nil { + return nil, false, err + } + + if null { + return nil, true, nil + } + + b := make([]byte, size) + if err := rd.ReadFull(b); err != nil { + return nil, false, err + } + return b, false, nil +} + +func readUtf8(rd *bufio.Reader) ([]byte, bool, error) { + size, null, err := readBytesSize(rd) + if err != nil { + return nil, false, err + } + + if null { + return nil, true, nil + } + + b, err := rd.ReadCesu8(size) + if err != nil { + return nil, false, err + } + + return b, false, nil +} + +// strings with one byte length +func readShortUtf8(rd *bufio.Reader) ([]byte, int, error) { + size, err := rd.ReadByte() + if err != nil { + return nil, 0, err + } + + b, err := rd.ReadCesu8(int(size)) + if err != nil { + return nil, 0, err + } + + return b, int(size), nil +} + +func writeBytes(wr *bufio.Writer, b []byte) error { + if err := writeBytesSize(wr, len(b)); err != nil { + return err + } + _, err := wr.Write(b) + return err +} + +func writeString(wr *bufio.Writer, s string) error { + if err := writeBytesSize(wr, len(s)); err != nil { + return err + } + _, err := wr.WriteString(s) + return err +} + +func writeUtf8Bytes(wr *bufio.Writer, b []byte) error { + size := cesu8.Size(b) + if err := writeBytesSize(wr, size); err != nil { + return err + } + _, err := wr.WriteCesu8(b) + return err +} + +func writeUtf8String(wr *bufio.Writer, s string) error { + size := cesu8.StringSize(s) + if err := writeBytesSize(wr, size); err != nil { + return err + } + _, err := wr.WriteStringCesu8(s) + return err +} + +func readLob(rd *bufio.Reader, tc typeCode) (bool, lobWriter, error) { + + if _, err := rd.ReadInt8(); err != nil { // type code (is int here) + return false, nil, err + } + + opt, err := rd.ReadInt8() + if err != nil { + return false, nil, err + } + + if err := rd.Skip(2); err != nil { + return false, nil, err + } + + charLen, err := rd.ReadInt64() + if err != nil { + return false, nil, err + } + byteLen, err := rd.ReadInt64() + if err != nil { + return false, nil, err + } + id, err := rd.ReadUint64() + if err != nil { + return false, nil, err + } + chunkLen, err := rd.ReadInt32() + if err != nil { + return false, nil, err + } + + null := (lobOptions(opt) & loNullindicator) != 0 + eof := (lobOptions(opt) & loLastdata) != 0 + + var writer lobWriter + if tc.isCharBased() { + writer = newCharLobWriter(locatorID(id), charLen, byteLen) + } else { + writer = newBinaryLobWriter(locatorID(id), charLen, byteLen) + } + if err := writer.write(rd, int(chunkLen), eof); err != nil { + return null, writer, err + } + return null, writer, nil +} + +// TODO: first write: add content? - actually no data transferred +func writeLob(wr *bufio.Writer) error { + + if err := wr.WriteByte(0); err != nil { + return err + } + if err := wr.WriteInt32(0); err != nil { + return err + } + if err := wr.WriteInt32(0); err != nil { + return err + } + return nil +} diff --git a/vendor/github.com/SAP/go-hdb/internal/protocol/functioncode.go b/vendor/github.com/SAP/go-hdb/internal/protocol/functioncode.go new file mode 100644 index 000000000000..87d765874270 --- /dev/null +++ b/vendor/github.com/SAP/go-hdb/internal/protocol/functioncode.go @@ -0,0 +1,60 @@ +/* +Copyright 2014 SAP SE + +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 protocol + +//go:generate stringer -type=functionCode + +type functionCode int16 + +const ( + fcNil functionCode = 0 + fcDDL functionCode = 1 + fcInsert functionCode = 2 + fcUpdate functionCode = 3 + fcDelete functionCode = 4 + fcSelect functionCode = 5 + fcSelectForUpdate functionCode = 6 + fcExplain functionCode = 7 + fcDBProcedureCall functionCode = 8 + fcDBProcedureCallWithResult functionCode = 9 + fcFetch functionCode = 10 + fcCommit functionCode = 11 + fcRollback functionCode = 12 + fcSavepoint functionCode = 13 + fcConnect functionCode = 14 + fcWriteLob functionCode = 15 + fcReadLob functionCode = 16 + fcPing functionCode = 17 //reserved: do not use + fcDisconnect functionCode = 18 + fcCloseCursor functionCode = 19 + fcFindLob functionCode = 20 + fcAbapStream functionCode = 21 + fcXAStart functionCode = 22 + fcXAJoin functionCode = 23 +) + +func (k functionCode) queryType() QueryType { + + switch k { + default: + return QtNone + case fcSelect, fcSelectForUpdate: + return QtSelect + case fcDBProcedureCall: + return QtProcedureCall + } +} diff --git a/vendor/github.com/SAP/go-hdb/internal/protocol/functioncode_string.go b/vendor/github.com/SAP/go-hdb/internal/protocol/functioncode_string.go new file mode 100644 index 000000000000..cc111d8ed86e --- /dev/null +++ b/vendor/github.com/SAP/go-hdb/internal/protocol/functioncode_string.go @@ -0,0 +1,16 @@ +// generated by stringer -type=functionCode; DO NOT EDIT + +package protocol + +import "fmt" + +const _functionCode_name = "fcNilfcDDLfcInsertfcUpdatefcDeletefcSelectfcSelectForUpdatefcExplainfcDBProcedureCallfcDBProcedureCallWithResultfcFetchfcCommitfcRollbackfcSavepointfcConnectfcWriteLobfcReadLobfcPingfcDisconnectfcCloseCursorfcFindLobfcAbapStreamfcXAStartfcXAJoin" + +var _functionCode_index = [...]uint8{0, 5, 10, 18, 26, 34, 42, 59, 68, 85, 112, 119, 127, 137, 148, 157, 167, 176, 182, 194, 207, 216, 228, 237, 245} + +func (i functionCode) String() string { + if i < 0 || i >= functionCode(len(_functionCode_index)-1) { + return fmt.Sprintf("functionCode(%d)", i) + } + return _functionCode_name[_functionCode_index[i]:_functionCode_index[i+1]] +} diff --git a/vendor/github.com/SAP/go-hdb/internal/protocol/init.go b/vendor/github.com/SAP/go-hdb/internal/protocol/init.go new file mode 100644 index 000000000000..f09cfac73e23 --- /dev/null +++ b/vendor/github.com/SAP/go-hdb/internal/protocol/init.go @@ -0,0 +1,268 @@ +/* +Copyright 2014 SAP SE + +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 protocol + +import ( + "fmt" + + "github.com/SAP/go-hdb/internal/bufio" +) + +const ( + okEndianess int8 = 1 +) + +const ( + initRequestFillerSize = 4 +) + +var initRequestFiller uint32 = 0xffffffff + +type productVersion struct { + major int8 + minor int16 +} + +func (v *productVersion) String() string { + return fmt.Sprintf("%d.%d", v.major, v.minor) +} + +type protocolVersion struct { + major int8 + minor int16 +} + +func (v *protocolVersion) String() string { + return fmt.Sprintf("%d.%d", v.major, v.minor) +} + +type version struct { + major int8 + minor int16 +} + +func (v *version) String() string { + return fmt.Sprintf("%d.%d", v.major, v.minor) +} + +type initRequest struct { + product *version + protocol *version + numOptions int8 + endianess endianess +} + +func newInitRequest() *initRequest { + return &initRequest{ + product: new(version), + protocol: new(version), + } +} + +func (r *initRequest) String() string { + switch r.numOptions { + default: + return fmt.Sprintf("init request: product version %s protocol version %s", r.product, r.protocol) + case 1: + return fmt.Sprintf("init request: product version %s protocol version %s endianess %s", r.product, r.protocol, r.endianess) + } +} + +func (r *initRequest) read(rd *bufio.Reader) error { + var err error + + if err := rd.Skip(initRequestFillerSize); err != nil { //filler + return err + } + + if r.product.major, err = rd.ReadInt8(); err != nil { + return err + } + if r.product.minor, err = rd.ReadInt16(); err != nil { + return err + } + if r.protocol.major, err = rd.ReadInt8(); err != nil { + return err + } + if r.protocol.minor, err = rd.ReadInt16(); err != nil { + return err + } + if err := rd.Skip(1); err != nil { //reserved filler + return err + } + if r.numOptions, err = rd.ReadInt8(); err != nil { + return err + } + + switch r.numOptions { + default: + outLogger.Fatalf("invalid number of options %d", r.numOptions) + + case 0: + if err := rd.Skip(2); err != nil { + return err + } + + case 1: + if cnt, err := rd.ReadInt8(); err == nil { + if cnt != 1 { + return fmt.Errorf("endianess %d - 1 expected", cnt) + } + } else { + return err + } + _endianess, err := rd.ReadInt8() + if err != nil { + return err + } + r.endianess = endianess(_endianess) + } + + if trace { + outLogger.Printf("read %s", r) + } + + return nil +} + +func (r *initRequest) write(wr *bufio.Writer) error { + + if err := wr.WriteUint32(initRequestFiller); err != nil { + return err + } + if err := wr.WriteInt8(r.product.major); err != nil { + return err + } + if err := wr.WriteInt16(r.product.minor); err != nil { + return err + } + if err := wr.WriteInt8(r.protocol.major); err != nil { + return err + } + if err := wr.WriteInt16(r.protocol.minor); err != nil { + return err + } + + switch r.numOptions { + default: + outLogger.Fatalf("invalid number of options %d", r.numOptions) + + case 0: + if err := wr.WriteZeroes(4); err != nil { + return err + } + + case 1: + // reserved + if err := wr.WriteZeroes(1); err != nil { + return err + } + if err := wr.WriteInt8(r.numOptions); err != nil { + return err + } + if err := wr.WriteInt8(int8(okEndianess)); err != nil { + return err + } + if err := wr.WriteInt8(int8(r.endianess)); err != nil { + return err + } + } + + // flush + if err := wr.Flush(); err != nil { + return err + } + + if trace { + outLogger.Printf("write %s", r) + } + + return nil +} + +type initReply struct { + product *version + protocol *version +} + +func newInitReply() *initReply { + return &initReply{ + product: new(version), + protocol: new(version), + } +} + +func (r *initReply) String() string { + return fmt.Sprintf("init reply: product version %s protocol version %s", r.product, r.protocol) +} + +func (r *initReply) read(rd *bufio.Reader) error { + var err error + + if r.product.major, err = rd.ReadInt8(); err != nil { + return err + } + if r.product.minor, err = rd.ReadInt16(); err != nil { + return err + } + if r.protocol.major, err = rd.ReadInt8(); err != nil { + return err + } + if r.protocol.minor, err = rd.ReadInt16(); err != nil { + return err + } + + if err := rd.Skip(2); err != nil { //commitInitReplySize + return err + } + + if trace { + outLogger.Printf("read %s", r) + } + + return nil +} + +func (r *initReply) write(wr *bufio.Writer) error { + if err := wr.WriteInt8(r.product.major); err != nil { + return err + } + if err := wr.WriteInt16(r.product.minor); err != nil { + return err + } + if err := wr.WriteInt8(r.product.major); err != nil { + return err + } + if err := wr.WriteInt16(r.protocol.minor); err != nil { + return err + } + + if err := wr.WriteZeroes(2); err != nil { // commitInitReplySize + return err + } + + // flush + if err := wr.Flush(); err != nil { + return err + } + + if trace { + outLogger.Printf("write %s", r) + } + + return nil +} diff --git a/vendor/github.com/SAP/go-hdb/internal/protocol/littleendian.go b/vendor/github.com/SAP/go-hdb/internal/protocol/littleendian.go new file mode 100644 index 000000000000..2775aaa25fb9 --- /dev/null +++ b/vendor/github.com/SAP/go-hdb/internal/protocol/littleendian.go @@ -0,0 +1,23 @@ +// +build amd64 386 arm arm64 + +/* +Copyright 2014 SAP SE + +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 protocol + +//amd64, 386 architectures: little endian +//arm, arm64: go supports little endian only +var archEndian = littleEndian diff --git a/vendor/github.com/SAP/go-hdb/internal/protocol/lob.go b/vendor/github.com/SAP/go-hdb/internal/protocol/lob.go new file mode 100644 index 000000000000..742e6a88fadd --- /dev/null +++ b/vendor/github.com/SAP/go-hdb/internal/protocol/lob.go @@ -0,0 +1,589 @@ +/* +Copyright 2014 SAP SE + +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 protocol + +import ( + "fmt" + "io" + "math" + "unicode/utf8" + + "golang.org/x/text/transform" + + "github.com/SAP/go-hdb/internal/bufio" + "github.com/SAP/go-hdb/internal/unicode" + "github.com/SAP/go-hdb/internal/unicode/cesu8" +) + +const ( + locatorIDSize = 8 + writeLobRequestHeaderSize = 21 + readLobRequestSize = 24 +) + +// variable (unit testing) +//var lobChunkSize = 1 << 14 //TODO: check size +var lobChunkSize int32 = 256 //TODO: check size + +//lob options +type lobOptions int8 + +const ( + loNullindicator lobOptions = 0x01 + loDataincluded lobOptions = 0x02 + loLastdata lobOptions = 0x04 +) + +var lobOptionsText = map[lobOptions]string{ + loNullindicator: "null indicator", + loDataincluded: "data included", + loLastdata: "last data", +} + +func (k lobOptions) String() string { + t := make([]string, 0, len(lobOptionsText)) + + for option, text := range lobOptionsText { + if (k & option) != 0 { + t = append(t, text) + } + } + return fmt.Sprintf("%v", t) +} + +// LobReadDescr is the package internal representation of a lob field to be read from database. +type LobReadDescr struct { + col int + fn func() error + w lobWriter +} + +// SetWriter sets the io.Writer destination for a lob field to be read from database. +func (d *LobReadDescr) SetWriter(w io.Writer) error { + if err := d.w.setWriter(w); err != nil { + return err + } + if d.fn != nil { + return d.fn() + } + return nil +} + +// LobWriteDescr is the package internal representation of a lob field to be written to database. +type LobWriteDescr struct { + r io.Reader +} + +// SetReader sets the io.Reader source for a lob field to be written to database. +func (d *LobWriteDescr) SetReader(r io.Reader) { + d.r = r +} + +type locatorID uint64 // byte[locatorIdSize] + +// write lob reply +type writeLobReply struct { + ids []locatorID + numArg int +} + +func newWriteLobReply() *writeLobReply { + return &writeLobReply{ + ids: make([]locatorID, 0), + } +} + +func (r *writeLobReply) String() string { + return fmt.Sprintf("write lob reply: %v", r.ids) +} + +func (r *writeLobReply) kind() partKind { + return pkWriteLobReply +} + +func (r *writeLobReply) setNumArg(numArg int) { + r.numArg = numArg +} + +func (r *writeLobReply) read(rd *bufio.Reader) error { + + //resize ids + if cap(r.ids) < r.numArg { + r.ids = make([]locatorID, r.numArg) + } else { + r.ids = r.ids[:r.numArg] + } + + for i := 0; i < r.numArg; i++ { + if id, err := rd.ReadUint64(); err == nil { + r.ids[i] = locatorID(id) + } else { + return err + } + } + + return nil +} + +//write lob request +type writeLobRequest struct { + readers []lobReader +} + +func newWriteLobRequest(readers []lobReader) *writeLobRequest { + return &writeLobRequest{ + readers: readers, + } +} + +func (r *writeLobRequest) kind() partKind { + return pkWriteLobRequest +} + +func (r *writeLobRequest) size() (int, error) { + + // TODO: check size limit + + size := 0 + for _, reader := range r.readers { + if reader.done() { + continue + } + + if err := reader.fill(); err != nil { + return 0, err + } + size += writeLobRequestHeaderSize + size += reader.size() + } + return size, nil +} + +func (r *writeLobRequest) numArg() int { + n := 0 + for _, reader := range r.readers { + if !reader.done() { + n++ + } + } + return n +} + +func (r *writeLobRequest) write(wr *bufio.Writer) error { + for _, reader := range r.readers { + if !reader.done() { + + if err := wr.WriteUint64(uint64(reader.id())); err != nil { + return err + } + + opt := int8(0x02) // data included + if reader.eof() { + opt |= 0x04 // last data + } + + if err := wr.WriteInt8(opt); err != nil { + return err + } + + if err := wr.WriteInt64(-1); err != nil { //offset (-1 := append) + return err + } + + if err := wr.WriteInt32(int32(reader.size())); err != nil { // size + return err + } + + if _, err := wr.Write(reader.bytes()); err != nil { + return err + } + } + } + return nil +} + +//read lob request +type readLobRequest struct { + writers []lobWriter +} + +func (r *readLobRequest) numWriter() int { + n := 0 + for _, writer := range r.writers { + if !writer.eof() { + n++ + } + } + return n +} + +func (r *readLobRequest) kind() partKind { + return pkReadLobRequest +} + +func (r *readLobRequest) size() (int, error) { + return r.numWriter() * readLobRequestSize, nil +} + +func (r *readLobRequest) numArg() int { + return r.numWriter() +} + +func (r *readLobRequest) write(wr *bufio.Writer) error { + for _, writer := range r.writers { + if writer.eof() { + continue + } + + if err := wr.WriteUint64(uint64(writer.id())); err != nil { + return err + } + + readOfs, readLen := writer.readOfsLen() + + if err := wr.WriteInt64(readOfs + 1); err != nil { //1-based + return err + } + + if err := wr.WriteInt32(readLen); err != nil { + return err + } + + if err := wr.WriteZeroes(4); err != nil { + return err + } + } + return nil +} + +// read lob reply +// - seems like readLobreply gives only an result for one lob - even if more then one is requested +type readLobReply struct { + writers []lobWriter + numArg int +} + +func (r *readLobReply) kind() partKind { + return pkReadLobReply +} + +func (r *readLobReply) setNumArg(numArg int) { + r.numArg = numArg +} + +func (r *readLobReply) read(rd *bufio.Reader) error { + for i := 0; i < r.numArg; i++ { + + id, err := rd.ReadUint64() + if err != nil { + return err + } + + var writer lobWriter + for _, writer = range r.writers { + if writer.id() == locatorID(id) { + break // writer found + } + } + if writer == nil { + return fmt.Errorf("internal error: no lob writer found for id %d", id) + } + + opt, err := rd.ReadInt8() + if err != nil { + return err + } + + chunkLen, err := rd.ReadInt32() + if err != nil { + return err + } + + if err := rd.Skip(3); err != nil { + return err + } + + eof := (lobOptions(opt) & loLastdata) != 0 + + if err := writer.write(rd, int(chunkLen), eof); err != nil { + return err + } + } + return nil +} + +// lobWriter reads lob chunks and writes them into lob field. +type lobWriter interface { + id() locatorID + setWriter(w io.Writer) error + write(rd *bufio.Reader, size int, eof bool) error + readOfsLen() (int64, int32) + eof() bool +} + +// baseLobWriter is a reuse struct for binary and char lob writers. +type baseLobWriter struct { + _id locatorID + charLen int64 + byteLen int64 + + readOfs int64 + _eof bool + + ofs int + + wr io.Writer + + _flush func() error + + b []byte +} + +func (l *baseLobWriter) id() locatorID { + return l._id +} + +func (l *baseLobWriter) eof() bool { + return l._eof +} + +func (l *baseLobWriter) setWriter(wr io.Writer) error { + l.wr = wr + return l._flush() +} + +func (l *baseLobWriter) write(rd *bufio.Reader, size int, eof bool) error { + l._eof = eof // store eof + + if size == 0 { + return nil + } + + l.b = resizeBuffer(l.b, size+l.ofs) + if err := rd.ReadFull(l.b[l.ofs:]); err != nil { + return err + } + if l.wr != nil { + return l._flush() + } + return nil +} + +func (l *baseLobWriter) readOfsLen() (int64, int32) { + readLen := l.charLen - l.readOfs + if readLen > int64(math.MaxInt32) || readLen > int64(lobChunkSize) { + return l.readOfs, lobChunkSize + } + return l.readOfs, int32(readLen) +} + +// binaryLobWriter (byte based lobs). +type binaryLobWriter struct { + *baseLobWriter +} + +func newBinaryLobWriter(id locatorID, charLen, byteLen int64) *binaryLobWriter { + l := &binaryLobWriter{ + baseLobWriter: &baseLobWriter{_id: id, charLen: charLen, byteLen: byteLen}, + } + l._flush = l.flush + return l +} + +func (l *binaryLobWriter) flush() error { + if _, err := l.wr.Write(l.b); err != nil { + return err + } + l.readOfs += int64(len(l.b)) + return nil +} + +type charLobWriter struct { + *baseLobWriter +} + +func newCharLobWriter(id locatorID, charLen, byteLen int64) *charLobWriter { + l := &charLobWriter{ + baseLobWriter: &baseLobWriter{_id: id, charLen: charLen, byteLen: byteLen}, + } + l._flush = l.flush + return l +} + +func (l *charLobWriter) flush() error { + nDst, nSrc, err := unicode.Cesu8ToUtf8Transformer.Transform(l.b, l.b, true) // inline cesu8 to utf8 transformation + if err != nil && err != transform.ErrShortSrc { + return err + } + if _, err := l.wr.Write(l.b[:nDst]); err != nil { + return err + } + l.ofs = len(l.b) - nSrc + if l.ofs != 0 && l.ofs != cesu8.CESUMax/2 { // assert remaining bytes + return unicode.ErrInvalidCesu8 + } + l.readOfs += int64(l.runeCount(l.b[:nDst])) + if l.ofs != 0 { + l.readOfs++ // add half encoding + copy(l.b, l.b[nSrc:len(l.b)]) // move half encoding to buffer begin + } + return nil +} + +// Caution: hdb counts 4 byte utf-8 encodings (cesu-8 6 bytes) as 2 (3 byte) chars +func (l *charLobWriter) runeCount(b []byte) int { + numChars := 0 + for len(b) > 0 { + _, size := utf8.DecodeRune(b) + b = b[size:] + numChars++ + if size == utf8.UTFMax { + numChars++ + } + } + return numChars +} + +// lobWriter reads field lob data chunks. +type lobReader interface { + id() locatorID + fill() error + size() int + bytes() []byte + eof() bool + done() bool +} + +// baseLobWriter is a reuse struct for binary and char lob writers. +type baseLobReader struct { + r io.Reader + _id locatorID + _size int + _eof bool + _done bool + b []byte +} + +func (l *baseLobReader) id() locatorID { + return l._id +} + +func (l *baseLobReader) eof() bool { + return l._eof +} + +func (l *baseLobReader) done() bool { + return l._done +} + +func (l *baseLobReader) size() int { + return l._size +} + +func (l *baseLobReader) bytes() []byte { + if l._eof { + l._done = true + } + return l.b[:l._size] +} + +// binaryLobReader (byte based lobs). +type binaryLobReader struct { + *baseLobReader +} + +func newBinaryLobReader(r io.Reader, id locatorID) *binaryLobReader { + return &binaryLobReader{ + baseLobReader: &baseLobReader{r: r, _id: id}, + } +} + +func (l *binaryLobReader) fill() error { + if l._eof { + return fmt.Errorf("locator id %d eof error", l._id) + } + + var err error + + l.b = resizeBuffer(l.b, int(lobChunkSize)) + l._size, err = l.r.Read(l.b) + if err != nil && err != io.EOF { + return err + } + l._eof = err == io.EOF + return nil +} + +// charLobReader (character based lobs - cesu8). +type charLobReader struct { + *baseLobReader + c []byte + ofs int +} + +func newCharLobReader(r io.Reader, id locatorID) *charLobReader { + return &charLobReader{ + baseLobReader: &baseLobReader{r: r, _id: id}, + } +} + +func (l *charLobReader) fill() error { + if l._eof { + return fmt.Errorf("locator id %d eof error", l._id) + } + + l.c = resizeBuffer(l.c, int(lobChunkSize)+l.ofs) + n, err := l.r.Read(l.c[l.ofs:]) + size := n + l.ofs + + if err != nil && err != io.EOF { + return err + } + l._eof = err == io.EOF + if l._eof && size == 0 { + l._size = 0 + return nil + } + + l.b = resizeBuffer(l.b, cesu8.Size(l.c[:size])) // last rune might be incomplete, so size is one greater than needed + nDst, nSrc, err := unicode.Utf8ToCesu8Transformer.Transform(l.b, l.c[:size], l._eof) + if err != nil && err != transform.ErrShortSrc { + return err + } + + if l._eof && err == transform.ErrShortSrc { + return unicode.ErrInvalidUtf8 + } + + l._size = nDst + l.ofs = size - nSrc + + if l.ofs > 0 { + copy(l.c, l.c[nSrc:size]) // copy rest to buffer beginn + } + return nil +} + +// helper +func resizeBuffer(b1 []byte, size int) []byte { + if b1 == nil || cap(b1) < size { + b2 := make([]byte, size) + copy(b2, b1) // !!! + return b2 + } + return b1[:size] +} diff --git a/vendor/github.com/SAP/go-hdb/internal/protocol/message.go b/vendor/github.com/SAP/go-hdb/internal/protocol/message.go new file mode 100644 index 000000000000..681d0708abc8 --- /dev/null +++ b/vendor/github.com/SAP/go-hdb/internal/protocol/message.go @@ -0,0 +1,103 @@ +/* +Copyright 2014 SAP SE + +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 protocol + +import ( + "fmt" + + "github.com/SAP/go-hdb/internal/bufio" +) + +const ( + messageHeaderSize = 32 +) + +//message header +type messageHeader struct { + sessionID int64 + packetCount int32 + varPartLength uint32 + varPartSize uint32 + noOfSegm int16 +} + +func (h *messageHeader) String() string { + return fmt.Sprintf("session id %d packetCount %d varPartLength %d, varPartSize %d noOfSegm %d", + h.sessionID, + h.packetCount, + h.varPartLength, + h.varPartSize, + h.noOfSegm) +} + +func (h *messageHeader) write(wr *bufio.Writer) error { + if err := wr.WriteInt64(h.sessionID); err != nil { + return err + } + if err := wr.WriteInt32(h.packetCount); err != nil { + return err + } + if err := wr.WriteUint32(h.varPartLength); err != nil { + return err + } + if err := wr.WriteUint32(h.varPartSize); err != nil { + return err + } + if err := wr.WriteInt16(h.noOfSegm); err != nil { + return err + } + + if err := wr.WriteZeroes(10); err != nil { //messageHeaderSize + return err + } + + if trace { + outLogger.Printf("write message header: %s", h) + } + + return nil +} + +func (h *messageHeader) read(rd *bufio.Reader) error { + var err error + + if h.sessionID, err = rd.ReadInt64(); err != nil { + return err + } + if h.packetCount, err = rd.ReadInt32(); err != nil { + return err + } + if h.varPartLength, err = rd.ReadUint32(); err != nil { + return err + } + if h.varPartSize, err = rd.ReadUint32(); err != nil { + return err + } + if h.noOfSegm, err = rd.ReadInt16(); err != nil { + return err + } + + if err := rd.Skip(10); err != nil { //messageHeaderSize + return err + } + + if trace { + outLogger.Printf("read message header: %s", h) + } + + return nil +} diff --git a/vendor/github.com/SAP/go-hdb/internal/protocol/messagetype.go b/vendor/github.com/SAP/go-hdb/internal/protocol/messagetype.go new file mode 100644 index 000000000000..cba80543233f --- /dev/null +++ b/vendor/github.com/SAP/go-hdb/internal/protocol/messagetype.go @@ -0,0 +1,49 @@ +/* +Copyright 2014 SAP SE + +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 protocol + +//go:generate stringer -type=messageType + +type messageType int8 + +const ( + mtNil messageType = 0 + mtExecuteDirect messageType = 2 + mtPrepare messageType = 3 + mtAbapStream messageType = 4 + mtXAStart messageType = 5 + mtXAJoin messageType = 6 + mtExecute messageType = 13 + mtWriteLob messageType = 16 + mtReadLob messageType = 17 + mtFindLob messageType = 18 + mtAuthenticate messageType = 65 + mtConnect messageType = 66 + mtCommit messageType = 67 + mtRollback messageType = 68 + mtCloseResultset messageType = 69 + mtDropStatementID messageType = 70 + mtFetchNext messageType = 71 + mtFetchAbsolute messageType = 72 + mtFetchRelative messageType = 73 + mtFetchFirst messageType = 74 + mtFetchLast messageType = 75 + mtDisconnect messageType = 77 + mtExecuteITab messageType = 78 + mtFetchNextITab messageType = 79 + mtInsertNextITab messageType = 80 +) diff --git a/vendor/github.com/SAP/go-hdb/internal/protocol/messagetype_string.go b/vendor/github.com/SAP/go-hdb/internal/protocol/messagetype_string.go new file mode 100644 index 000000000000..c6a7156b595a --- /dev/null +++ b/vendor/github.com/SAP/go-hdb/internal/protocol/messagetype_string.go @@ -0,0 +1,46 @@ +// generated by stringer -type=messageType; DO NOT EDIT + +package protocol + +import "fmt" + +const ( + _messageType_name_0 = "mtNil" + _messageType_name_1 = "mtExecuteDirectmtPreparemtAbapStreammtXAStartmtXAJoin" + _messageType_name_2 = "mtExecute" + _messageType_name_3 = "mtWriteLobmtReadLobmtFindLob" + _messageType_name_4 = "mtAuthenticatemtConnectmtCommitmtRollbackmtCloseResultsetmtDropStatementIDmtFetchNextmtFetchAbsolutemtFetchRelativemtFetchFirstmtFetchLast" + _messageType_name_5 = "mtDisconnectmtExecuteITabmtFetchNextITabmtInsertNextITab" +) + +var ( + _messageType_index_0 = [...]uint8{0, 5} + _messageType_index_1 = [...]uint8{0, 15, 24, 36, 45, 53} + _messageType_index_2 = [...]uint8{0, 9} + _messageType_index_3 = [...]uint8{0, 10, 19, 28} + _messageType_index_4 = [...]uint8{0, 14, 23, 31, 41, 57, 74, 85, 100, 115, 127, 138} + _messageType_index_5 = [...]uint8{0, 12, 25, 40, 56} +) + +func (i messageType) String() string { + switch { + case i == 0: + return _messageType_name_0 + case 2 <= i && i <= 6: + i -= 2 + return _messageType_name_1[_messageType_index_1[i]:_messageType_index_1[i+1]] + case i == 13: + return _messageType_name_2 + case 16 <= i && i <= 18: + i -= 16 + return _messageType_name_3[_messageType_index_3[i]:_messageType_index_3[i+1]] + case 65 <= i && i <= 75: + i -= 65 + return _messageType_name_4[_messageType_index_4[i]:_messageType_index_4[i+1]] + case 77 <= i && i <= 80: + i -= 77 + return _messageType_name_5[_messageType_index_5[i]:_messageType_index_5[i+1]] + default: + return fmt.Sprintf("messageType(%d)", i) + } +} diff --git a/vendor/github.com/SAP/go-hdb/internal/protocol/option.go b/vendor/github.com/SAP/go-hdb/internal/protocol/option.go new file mode 100644 index 000000000000..61a81fc1979e --- /dev/null +++ b/vendor/github.com/SAP/go-hdb/internal/protocol/option.go @@ -0,0 +1,270 @@ +/* +Copyright 2014 SAP SE + +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 protocol + +import ( + "fmt" + + "github.com/SAP/go-hdb/internal/bufio" +) + +type booleanType bool + +func (t booleanType) String() string { + return fmt.Sprintf("%t", t) +} + +type intType int32 + +func (t intType) String() string { + return fmt.Sprintf("%d", t) +} + +type bigintType int64 + +func (t bigintType) String() string { + return fmt.Sprintf("%d", t) +} + +type doubleType float64 + +func (t doubleType) String() string { + return fmt.Sprintf("%g", t) +} + +type stringType []byte + +type binaryStringType []byte + +func (t binaryStringType) String() string { + return fmt.Sprintf("%v", []byte(t)) +} + +//multi line options (number of lines in part header argumentCount) +type multiLineOptions []plainOptions + +func (o multiLineOptions) size() int { + size := 0 + for _, m := range o { + size += m.size() + } + return size +} + +//pointer: append multiLineOptions itself +func (o *multiLineOptions) read(rd *bufio.Reader, lineCnt int) error { + + for i := 0; i < lineCnt; i++ { + + m := plainOptions{} + + cnt, err := rd.ReadInt16() + if err != nil { + return err + } + if err := m.read(rd, int(cnt)); err != nil { + return err + } + + *o = append(*o, m) + } + return nil +} + +func (o multiLineOptions) write(wr *bufio.Writer) error { + for _, m := range o { + + if err := wr.WriteInt16(int16(len(m))); err != nil { + return err + } + if err := m.write(wr); err != nil { + return err + } + } + return nil +} + +type plainOptions map[int8]interface{} + +func (o plainOptions) size() int { + size := 2 * len(o) //option + type + for _, v := range o { + switch v := v.(type) { + default: + outLogger.Fatalf("type %T not implemented", v) + case booleanType: + size++ + case intType: + size += 4 + case bigintType: + size += 8 + case doubleType: + size += 8 + case stringType: + size += (2 + len(v)) //length int16 + string length + case binaryStringType: + size += (2 + len(v)) //length int16 + string length + } + } + return size +} + +func (o plainOptions) read(rd *bufio.Reader, cnt int) error { + + for i := 0; i < cnt; i++ { + + k, err := rd.ReadInt8() + if err != nil { + return err + } + + tc, err := rd.ReadByte() + if err != nil { + return err + } + + switch typeCode(tc) { + + default: + outLogger.Fatalf("type code %s not implemented", typeCode(tc)) + + case tcBoolean: + if v, err := rd.ReadBool(); err == nil { + o[k] = booleanType(v) + } else { + return err + } + + case tcInt: + if v, err := rd.ReadInt32(); err == nil { + o[k] = intType(v) + } else { + return err + } + + case tcBigint: + if v, err := rd.ReadInt64(); err == nil { + o[k] = bigintType(v) + } else { + return err + } + + case tcDouble: + if v, err := rd.ReadFloat64(); err == nil { + o[k] = doubleType(v) + } else { + return err + } + + case tcString: + size, err := rd.ReadInt16() + if err != nil { + return err + } + v := make([]byte, size) + if err := rd.ReadFull(v); err == nil { + o[k] = stringType(v) + } else { + return err + } + + case tcBstring: + size, err := rd.ReadInt16() + if err != nil { + return err + } + v := make([]byte, size) + if err := rd.ReadFull(v); err == nil { + o[k] = binaryStringType(v) + } else { + return err + } + } + } + return nil +} + +func (o plainOptions) write(wr *bufio.Writer) error { + + for k, v := range o { + + if err := wr.WriteInt8(k); err != nil { + return err + } + + switch v := v.(type) { + + default: + outLogger.Fatalf("type %T not implemented", v) + + case booleanType: + if err := wr.WriteInt8(int8(tcBoolean)); err != nil { + return err + } + if err := wr.WriteBool(bool(v)); err != nil { + return err + } + + case intType: + if err := wr.WriteInt8(int8(tcInt)); err != nil { + return err + } + if err := wr.WriteInt32(int32(v)); err != nil { + return err + } + + case bigintType: + if err := wr.WriteInt8(int8(tcBigint)); err != nil { + return err + } + if err := wr.WriteInt64(int64(v)); err != nil { + return err + } + + case doubleType: + if err := wr.WriteInt8(int8(tcDouble)); err != nil { + return err + } + if err := wr.WriteFloat64(float64(v)); err != nil { + return err + } + + case stringType: + if err := wr.WriteInt8(int8(tcString)); err != nil { + return err + } + if err := wr.WriteInt16(int16(len(v))); err != nil { + return err + } + if _, err := wr.Write(v); err != nil { + return err + } + + case binaryStringType: + if err := wr.WriteInt8(int8(tcBstring)); err != nil { + return err + } + if err := wr.WriteInt16(int16(len(v))); err != nil { + return err + } + if _, err := wr.Write(v); err != nil { + return err + } + } + } + return nil +} diff --git a/vendor/github.com/SAP/go-hdb/internal/protocol/parameter.go b/vendor/github.com/SAP/go-hdb/internal/protocol/parameter.go new file mode 100644 index 000000000000..5c04a58daf69 --- /dev/null +++ b/vendor/github.com/SAP/go-hdb/internal/protocol/parameter.go @@ -0,0 +1,316 @@ +/* +Copyright 2014 SAP SE + +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 protocol + +import ( + "database/sql/driver" + "fmt" + + "github.com/SAP/go-hdb/internal/bufio" +) + +type parameterOptions int8 + +const ( + poMandatory parameterOptions = 0x01 + poOptional parameterOptions = 0x02 + poDefault parameterOptions = 0x04 +) + +var parameterOptionsText = map[parameterOptions]string{ + poMandatory: "mandatory", + poOptional: "optional", + poDefault: "default", +} + +func (k parameterOptions) String() string { + t := make([]string, 0, len(parameterOptionsText)) + + for option, text := range parameterOptionsText { + if (k & option) != 0 { + t = append(t, text) + } + } + return fmt.Sprintf("%v", t) +} + +type parameterMode int8 + +const ( + pmIn parameterMode = 0x01 + pmInout parameterMode = 0x02 + pmOut parameterMode = 0x04 +) + +var parameterModeText = map[parameterMode]string{ + pmIn: "in", + pmInout: "inout", + pmOut: "out", +} + +func (k parameterMode) String() string { + t := make([]string, 0, len(parameterModeText)) + + for mode, text := range parameterModeText { + if (k & mode) != 0 { + t = append(t, text) + } + } + return fmt.Sprintf("%v", t) +} + +type parameterField struct { + parameterOptions parameterOptions + tc typeCode + mode parameterMode + fraction int16 + length int16 + nameOffset uint32 +} + +func newParameterField() *parameterField { + return ¶meterField{} +} + +func (f *parameterField) String() string { + return fmt.Sprintf("parameterOptions %s typeCode %s mode %s fraction %d length %d nameOffset %d", + f.parameterOptions, + f.tc, + f.mode, + f.fraction, + f.length, + f.nameOffset, + ) +} + +// field interface +func (f *parameterField) typeCode() typeCode { + return f.tc +} + +func (f *parameterField) in() bool { + return f.mode == pmInout || f.mode == pmIn +} + +func (f *parameterField) out() bool { + return f.mode == pmInout || f.mode == pmOut +} + +func (f *parameterField) name(names map[uint32]string) string { + return names[f.nameOffset] +} + +func (f *parameterField) nameOffsets() []uint32 { + return []uint32{f.nameOffset} +} + +// + +func (f *parameterField) read(rd *bufio.Reader) error { + var err error + + if po, err := rd.ReadInt8(); err == nil { + f.parameterOptions = parameterOptions(po) + } else { + return err + } + if tc, err := rd.ReadInt8(); err == nil { + f.tc = typeCode(tc) + } else { + return err + } + if mode, err := rd.ReadInt8(); err == nil { + f.mode = parameterMode(mode) + } else { + return err + } + if err := rd.Skip(1); err != nil { //filler + return err + } + if f.nameOffset, err = rd.ReadUint32(); err != nil { + return err + } + if f.length, err = rd.ReadInt16(); err != nil { + return err + } + if f.fraction, err = rd.ReadInt16(); err != nil { + return err + } + if err := rd.Skip(4); err != nil { //filler + return err + } + return nil +} + +// parameter metadata +type parameterMetadata struct { + fieldSet *FieldSet + numArg int +} + +func (m *parameterMetadata) String() string { + return fmt.Sprintf("parameter metadata: %s", m.fieldSet.fields) +} + +func (m *parameterMetadata) kind() partKind { + return pkParameterMetadata +} + +func (m *parameterMetadata) setNumArg(numArg int) { + m.numArg = numArg +} + +func (m *parameterMetadata) read(rd *bufio.Reader) error { + + for i := 0; i < m.numArg; i++ { + field := newParameterField() + if err := field.read(rd); err != nil { + return err + } + m.fieldSet.fields[i] = field + } + + pos := uint32(0) + for _, offset := range m.fieldSet.nameOffsets() { + if diff := int(offset - pos); diff > 0 { + rd.Skip(diff) + } + + b, size, err := readShortUtf8(rd) + if err != nil { + return err + } + + m.fieldSet.names[offset] = string(b) + + pos += uint32(1 + size) + } + + if trace { + outLogger.Printf("read %s", m) + } + + return nil +} + +// parameters +type parameters struct { + fields []field //input fields + args []driver.Value +} + +func newParameters(fieldSet *FieldSet, args []driver.Value) *parameters { + m := ¶meters{ + fields: make([]field, 0, len(fieldSet.fields)), + args: args, + } + for _, field := range fieldSet.fields { + if field.in() { + m.fields = append(m.fields, field) + } + } + return m +} + +func (m *parameters) kind() partKind { + return pkParameters +} + +func (m *parameters) size() (int, error) { + + size := len(m.args) + cnt := len(m.fields) + + for i, arg := range m.args { + + if arg == nil { // null value + continue + } + + // mass insert + field := m.fields[i%cnt] + + fieldSize, err := fieldSize(field.typeCode(), arg) + if err != nil { + return 0, err + } + + size += fieldSize + } + + return size, nil +} + +func (m *parameters) numArg() int { + cnt := len(m.fields) + + if cnt == 0 { // avoid divide-by-zero (e.g. prepare without parameters) + return 0 + } + + return len(m.args) / cnt +} + +func (m parameters) write(wr *bufio.Writer) error { + + cnt := len(m.fields) + + for i, arg := range m.args { + + //mass insert + field := m.fields[i%cnt] + + if err := writeField(wr, field.typeCode(), arg); err != nil { + return err + } + } + + if trace { + outLogger.Printf("parameters: %s", m) + } + + return nil +} + +// output parameter +type outputParameters struct { + numArg int + fieldSet *FieldSet + fieldValues *FieldValues +} + +func (r *outputParameters) String() string { + return fmt.Sprintf("output parameters: %v", r.fieldValues) +} + +func (r *outputParameters) kind() partKind { + return pkOutputParameters +} + +func (r *outputParameters) setNumArg(numArg int) { + r.numArg = numArg // should always be 1 +} + +func (r *outputParameters) read(rd *bufio.Reader) error { + if err := r.fieldValues.read(r.numArg, r.fieldSet, rd); err != nil { + return err + } + if trace { + outLogger.Printf("read %s", r) + } + return nil +} diff --git a/vendor/github.com/SAP/go-hdb/internal/protocol/part.go b/vendor/github.com/SAP/go-hdb/internal/protocol/part.go new file mode 100644 index 000000000000..183519fa2784 --- /dev/null +++ b/vendor/github.com/SAP/go-hdb/internal/protocol/part.go @@ -0,0 +1,174 @@ +/* +Copyright 2014 SAP SE + +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 protocol + +import ( + "fmt" + + "github.com/SAP/go-hdb/internal/bufio" +) + +const ( + partHeaderSize = 16 +) + +type requestPart interface { + kind() partKind + size() (int, error) + numArg() int + write(*bufio.Writer) error +} + +type replyPart interface { + //kind() partKind + setNumArg(int) + read(*bufio.Reader) error +} + +// PartAttributes is an interface defining methods for reading query resultset parts. +type PartAttributes interface { + ResultsetClosed() bool + LastPacket() bool + NoRows() bool +} + +type partAttributes int8 + +const ( + paLastPacket partAttributes = 0x01 + paNextPacket partAttributes = 0x02 + paFirstPacket partAttributes = 0x04 + paRowNotFound partAttributes = 0x08 + paResultsetClosed partAttributes = 0x10 +) + +var partAttributesText = map[partAttributes]string{ + paLastPacket: "lastPacket", + paNextPacket: "nextPacket", + paFirstPacket: "firstPacket", + paRowNotFound: "rowNotFound", + paResultsetClosed: "resultsetClosed", +} + +func (k partAttributes) String() string { + t := make([]string, 0, len(partAttributesText)) + + for attr, text := range partAttributesText { + if (k & attr) != 0 { + t = append(t, text) + } + } + return fmt.Sprintf("%v", t) +} + +func (k partAttributes) ResultsetClosed() bool { + return (k & paResultsetClosed) == paResultsetClosed +} + +func (k partAttributes) LastPacket() bool { + return (k & paLastPacket) == paLastPacket +} + +func (k partAttributes) NoRows() bool { + attrs := paLastPacket | paRowNotFound + return (k & attrs) == attrs +} + +// part header +type partHeader struct { + partKind partKind + partAttributes partAttributes + argumentCount int16 + bigArgumentCount int32 + bufferLength int32 + bufferSize int32 +} + +func (h *partHeader) String() string { + return fmt.Sprintf("part kind %s partAttributes %s argumentCount %d bigArgumentCount %d bufferLength %d bufferSize %d", + h.partKind, + h.partAttributes, + h.argumentCount, + h.bigArgumentCount, + h.bufferLength, + h.bufferSize, + ) +} + +func (h *partHeader) write(wr *bufio.Writer) error { + if err := wr.WriteInt8(int8(h.partKind)); err != nil { + return err + } + if err := wr.WriteInt8(int8(h.partAttributes)); err != nil { + return err + } + if err := wr.WriteInt16(h.argumentCount); err != nil { + return err + } + if err := wr.WriteInt32(h.bigArgumentCount); err != nil { + return err + } + if err := wr.WriteInt32(h.bufferLength); err != nil { + return err + } + if err := wr.WriteInt32(h.bufferSize); err != nil { + return err + } + + //no filler + + if trace { + outLogger.Printf("write part header: %s", h) + } + + return nil +} + +func (h *partHeader) read(rd *bufio.Reader) error { + var err error + + if pk, err := rd.ReadInt8(); err == nil { + h.partKind = partKind(pk) + } else { + return err + } + if pa, err := rd.ReadInt8(); err == nil { + h.partAttributes = partAttributes(pa) + } else { + return err + } + if h.argumentCount, err = rd.ReadInt16(); err != nil { + return err + } + if h.bigArgumentCount, err = rd.ReadInt32(); err != nil { + return err + } + if h.bufferLength, err = rd.ReadInt32(); err != nil { + return err + } + if h.bufferSize, err = rd.ReadInt32(); err != nil { + return err + } + + // no filler + + if trace { + outLogger.Printf("read part header: %s", h) + } + + return nil +} diff --git a/vendor/github.com/SAP/go-hdb/internal/protocol/partkind.go b/vendor/github.com/SAP/go-hdb/internal/protocol/partkind.go new file mode 100644 index 000000000000..887ac73fed2e --- /dev/null +++ b/vendor/github.com/SAP/go-hdb/internal/protocol/partkind.go @@ -0,0 +1,65 @@ +/* +Copyright 2014 SAP SE + +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 protocol + +//go:generate stringer -type=partKind + +type partKind int8 + +const ( + pkNil partKind = 0 + pkCommand partKind = 3 + pkResultset partKind = 5 + pkError partKind = 6 + pkStatementID partKind = 10 + pkTransactionID partKind = 11 + pkRowsAffected partKind = 12 + pkResultsetID partKind = 13 + pkTopologyInformation partKind = 15 + pkTableLocation partKind = 16 + pkReadLobRequest partKind = 17 + pkReadLobReply partKind = 18 + pkAbapIStream partKind = 25 + pkAbapOStream partKind = 26 + pkCommandInfo partKind = 27 + pkWriteLobRequest partKind = 28 + pkWriteLobReply partKind = 30 + pkParameters partKind = 32 + pkAuthentication partKind = 33 + pkSessionContext partKind = 34 + pkStatementContext partKind = 39 + pkPartitionInformation partKind = 40 + pkOutputParameters partKind = 41 + pkConnectOptions partKind = 42 + pkCommitOptions partKind = 43 + pkFetchOptions partKind = 44 + pkFetchSize partKind = 45 + pkParameterMetadata partKind = 47 + pkResultMetadata partKind = 48 + pkFindLobRequest partKind = 49 + pkFindLobReply partKind = 50 + pkItabSHM partKind = 51 + pkItabChunkMetadata partKind = 53 + pkItabMetadata partKind = 55 + pkItabResultChunk partKind = 56 + pkClientInfo partKind = 57 + pkStreamData partKind = 58 + pkOStreamResult partKind = 59 + pkFDARequestMetadata partKind = 60 + pkFDAReplyMetadata partKind = 61 + pkTransactionFlags partKind = 64 +) diff --git a/vendor/github.com/SAP/go-hdb/internal/protocol/partkind_string.go b/vendor/github.com/SAP/go-hdb/internal/protocol/partkind_string.go new file mode 100644 index 000000000000..01040f2b3fdc --- /dev/null +++ b/vendor/github.com/SAP/go-hdb/internal/protocol/partkind_string.go @@ -0,0 +1,58 @@ +// generated by stringer -type=partKind; DO NOT EDIT + +package protocol + +import "fmt" + +const _partKind_name = "pkNilpkCommandpkResultsetpkErrorpkStatementIDpkTransactionIDpkRowsAffectedpkResultsetIDpkTopologyInformationpkTableLocationpkReadLobRequestpkReadLobReplypkAbapIStreampkAbapOStreampkCommandInfopkWriteLobRequestpkWriteLobReplypkParameterspkAuthenticationpkSessionContextpkStatementContextpkPartitionInformationpkOutputParameterspkConnectOptionspkCommitOptionspkFetchOptionspkFetchSizepkParameterMetadatapkResultMetadatapkFindLobRequestpkFindLobReplypkItabSHMpkItabChunkMetadatapkItabMetadatapkItabResultChunkpkClientInfopkStreamDatapkOStreamResultpkFDARequestMetadatapkFDAReplyMetadatapkTransactionFlags" + +var _partKind_map = map[partKind]string{ + 0: _partKind_name[0:5], + 3: _partKind_name[5:14], + 5: _partKind_name[14:25], + 6: _partKind_name[25:32], + 10: _partKind_name[32:45], + 11: _partKind_name[45:60], + 12: _partKind_name[60:74], + 13: _partKind_name[74:87], + 15: _partKind_name[87:108], + 16: _partKind_name[108:123], + 17: _partKind_name[123:139], + 18: _partKind_name[139:153], + 25: _partKind_name[153:166], + 26: _partKind_name[166:179], + 27: _partKind_name[179:192], + 28: _partKind_name[192:209], + 30: _partKind_name[209:224], + 32: _partKind_name[224:236], + 33: _partKind_name[236:252], + 34: _partKind_name[252:268], + 39: _partKind_name[268:286], + 40: _partKind_name[286:308], + 41: _partKind_name[308:326], + 42: _partKind_name[326:342], + 43: _partKind_name[342:357], + 44: _partKind_name[357:371], + 45: _partKind_name[371:382], + 47: _partKind_name[382:401], + 48: _partKind_name[401:417], + 49: _partKind_name[417:433], + 50: _partKind_name[433:447], + 51: _partKind_name[447:456], + 53: _partKind_name[456:475], + 55: _partKind_name[475:489], + 56: _partKind_name[489:506], + 57: _partKind_name[506:518], + 58: _partKind_name[518:530], + 59: _partKind_name[530:545], + 60: _partKind_name[545:565], + 61: _partKind_name[565:583], + 64: _partKind_name[583:601], +} + +func (i partKind) String() string { + if str, ok := _partKind_map[i]; ok { + return str + } + return fmt.Sprintf("partKind(%d)", i) +} diff --git a/vendor/github.com/SAP/go-hdb/internal/protocol/querytype.go b/vendor/github.com/SAP/go-hdb/internal/protocol/querytype.go new file mode 100644 index 000000000000..d32041af43ca --- /dev/null +++ b/vendor/github.com/SAP/go-hdb/internal/protocol/querytype.go @@ -0,0 +1,29 @@ +/* +Copyright 2014 SAP SE + +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 protocol + +//go:generate stringer -type=QueryType + +// QueryType is the type definition for query types supported by this package. +type QueryType byte + +// Query type constants. +const ( + QtNone QueryType = iota + QtSelect + QtProcedureCall +) diff --git a/vendor/github.com/SAP/go-hdb/internal/protocol/querytype_string.go b/vendor/github.com/SAP/go-hdb/internal/protocol/querytype_string.go new file mode 100644 index 000000000000..d821c546263a --- /dev/null +++ b/vendor/github.com/SAP/go-hdb/internal/protocol/querytype_string.go @@ -0,0 +1,16 @@ +// generated by stringer -type=QueryType; DO NOT EDIT + +package protocol + +import "fmt" + +const _QueryType_name = "QtNoneQtSelectQtProcedureCall" + +var _QueryType_index = [...]uint8{0, 6, 14, 29} + +func (i QueryType) String() string { + if i >= QueryType(len(_QueryType_index)-1) { + return fmt.Sprintf("QueryType(%d)", i) + } + return _QueryType_name[_QueryType_index[i]:_QueryType_index[i+1]] +} diff --git a/vendor/github.com/SAP/go-hdb/internal/protocol/result.go b/vendor/github.com/SAP/go-hdb/internal/protocol/result.go new file mode 100644 index 000000000000..2db8d9c71d9d --- /dev/null +++ b/vendor/github.com/SAP/go-hdb/internal/protocol/result.go @@ -0,0 +1,277 @@ +/* +Copyright 2014 SAP SE + +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 protocol + +import ( + "fmt" + + "github.com/SAP/go-hdb/internal/bufio" +) + +const ( + resultsetIDSize = 8 +) + +type columnOptions int8 + +const ( + coMandatory columnOptions = 0x01 + coOptional columnOptions = 0x02 +) + +var columnOptionsText = map[columnOptions]string{ + coMandatory: "mandatory", + coOptional: "optional", +} + +func (k columnOptions) String() string { + t := make([]string, 0, len(columnOptionsText)) + + for option, text := range columnOptionsText { + if (k & option) != 0 { + t = append(t, text) + } + } + return fmt.Sprintf("%v", t) +} + +//resultset id +type resultsetID struct { + id *uint64 +} + +func (id *resultsetID) kind() partKind { + return pkResultsetID +} + +func (id *resultsetID) size() (int, error) { + return resultsetIDSize, nil +} + +func (id *resultsetID) numArg() int { + return 1 +} + +func (id *resultsetID) setNumArg(int) { + //ignore - always 1 +} + +func (id *resultsetID) read(rd *bufio.Reader) error { + + _id, err := rd.ReadUint64() + if err != nil { + return err + } + *id.id = _id + + if trace { + outLogger.Printf("resultset id: %d", *id.id) + } + + return nil +} + +func (id *resultsetID) write(wr *bufio.Writer) error { + + if err := wr.WriteUint64(*id.id); err != nil { + return err + } + if trace { + outLogger.Printf("resultset id: %d", *id.id) + } + + return nil +} + +const ( + resultTableName = iota // used as index: start with 0 + resultSchemaName + resultColumnName + resultColumnDisplayName + maxResultNames +) + +type resultField struct { + columnOptions columnOptions + tc typeCode + fraction int16 + length int16 + tablenameOffset uint32 + schemanameOffset uint32 + columnnameOffset uint32 + columnDisplaynameOffset uint32 +} + +func newResultField() *resultField { + return &resultField{} +} + +func (f *resultField) String() string { + return fmt.Sprintf("columnsOptions %s typeCode %s fraction %d length %d tablenameOffset %d schemanameOffset %d columnnameOffset %d columnDisplaynameOffset %d", + f.columnOptions, + f.tc, + f.fraction, + f.length, + f.tablenameOffset, + f.schemanameOffset, + f.columnnameOffset, + f.columnDisplaynameOffset, + ) +} + +// Field interface +func (f *resultField) typeCode() typeCode { + return f.tc +} + +func (f *resultField) in() bool { + return false +} + +func (f *resultField) out() bool { + return true +} + +func (f *resultField) name(names map[uint32]string) string { + return names[f.columnnameOffset] +} + +func (f *resultField) nameOffsets() []uint32 { + return []uint32{f.tablenameOffset, f.schemanameOffset, f.columnnameOffset, f.columnDisplaynameOffset} +} + +// + +func (f *resultField) read(rd *bufio.Reader) error { + var err error + + if co, err := rd.ReadInt8(); err == nil { + f.columnOptions = columnOptions(co) + } else { + return err + } + if tc, err := rd.ReadInt8(); err == nil { + f.tc = typeCode(tc) + } else { + return err + } + if f.fraction, err = rd.ReadInt16(); err != nil { + return err + } + if f.length, err = rd.ReadInt16(); err != nil { + return err + } + + if err := rd.Skip(2); err != nil { //filler + return err + } + + if f.tablenameOffset, err = rd.ReadUint32(); err != nil { + return err + } + if f.schemanameOffset, err = rd.ReadUint32(); err != nil { + return err + } + if f.columnnameOffset, err = rd.ReadUint32(); err != nil { + return err + } + if f.columnDisplaynameOffset, err = rd.ReadUint32(); err != nil { + return err + } + + return nil +} + +//resultset metadata +type resultMetadata struct { + fieldSet *FieldSet + numArg int +} + +func (r *resultMetadata) String() string { + return fmt.Sprintf("result metadata: %s", r.fieldSet.fields) +} + +func (r *resultMetadata) kind() partKind { + return pkResultMetadata +} + +func (r *resultMetadata) setNumArg(numArg int) { + r.numArg = numArg +} + +func (r *resultMetadata) read(rd *bufio.Reader) error { + + for i := 0; i < r.numArg; i++ { + field := newResultField() + if err := field.read(rd); err != nil { + return err + } + r.fieldSet.fields[i] = field + } + + pos := uint32(0) + for _, offset := range r.fieldSet.nameOffsets() { + if diff := int(offset - pos); diff > 0 { + rd.Skip(diff) + } + + b, size, err := readShortUtf8(rd) + if err != nil { + return err + } + + r.fieldSet.names[offset] = string(b) + + pos += uint32(1 + size) + } + + if trace { + outLogger.Printf("read %s", r) + } + + return nil +} + +//resultset +type resultset struct { + numArg int + fieldSet *FieldSet + fieldValues *FieldValues +} + +func (r *resultset) String() string { + return fmt.Sprintf("resultset: %s", r.fieldValues) +} + +func (r *resultset) kind() partKind { + return pkResultset +} + +func (r *resultset) setNumArg(numArg int) { + r.numArg = numArg +} + +func (r *resultset) read(rd *bufio.Reader) error { + if err := r.fieldValues.read(r.numArg, r.fieldSet, rd); err != nil { + return err + } + if trace { + outLogger.Printf("read %s", r) + } + return nil +} diff --git a/vendor/github.com/SAP/go-hdb/internal/protocol/rowsaffected.go b/vendor/github.com/SAP/go-hdb/internal/protocol/rowsaffected.go new file mode 100644 index 000000000000..15b8c78fa0ef --- /dev/null +++ b/vendor/github.com/SAP/go-hdb/internal/protocol/rowsaffected.go @@ -0,0 +1,76 @@ +/* +Copyright 2014 SAP SE + +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 protocol + +import ( + "github.com/SAP/go-hdb/internal/bufio" +) + +//rows affected +const ( + raSuccessNoInfo = -2 + raExecutionFailed = -3 +) + +//rows affected +type rowsAffected struct { + sums []int32 + _numArg int +} + +func (r *rowsAffected) kind() partKind { + return pkRowsAffected +} + +func (r *rowsAffected) setNumArg(numArg int) { + r._numArg = numArg +} + +func (r *rowsAffected) read(rd *bufio.Reader) error { + if r.sums == nil || r._numArg > cap(r.sums) { + r.sums = make([]int32, r._numArg) + } else { + r.sums = r.sums[:r._numArg] + } + + var err error + + for i := 0; i < r._numArg; i++ { + r.sums[i], err = rd.ReadInt32() + if err != nil { + return err + } + } + + if trace { + outLogger.Printf("rows affected %v", r.sums) + } + + return nil +} + +func (r *rowsAffected) total() int64 { + if r.sums == nil { + return 0 + } + + total := int64(0) + for _, sum := range r.sums { + total += int64(sum) + } + return total +} diff --git a/vendor/github.com/SAP/go-hdb/internal/protocol/scramsha256.go b/vendor/github.com/SAP/go-hdb/internal/protocol/scramsha256.go new file mode 100644 index 000000000000..a9ca393c58f2 --- /dev/null +++ b/vendor/github.com/SAP/go-hdb/internal/protocol/scramsha256.go @@ -0,0 +1,332 @@ +/* +Copyright 2014 SAP SE + +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 protocol + +//Salted Challenge Response Authentication Mechanism (SCRAM) + +import ( + "crypto/hmac" + "crypto/rand" + "crypto/sha256" + "fmt" + + "github.com/SAP/go-hdb/internal/bufio" +) + +const ( + clientChallengeSize = 64 + serverChallengeDataSize = 68 + clientProofDataSize = 35 + clientProofSize = 32 +) + +type scramsha256InitialRequest struct { + username []byte + clientChallenge []byte +} + +func newScramsha256InitialRequest() *scramsha256InitialRequest { + return &scramsha256InitialRequest{} +} + +func (r *scramsha256InitialRequest) kind() partKind { + return pkAuthentication +} + +func (r *scramsha256InitialRequest) size() (int, error) { + return 2 + authFieldSize(r.username) + authFieldSize([]byte(mnSCRAMSHA256)) + authFieldSize(r.clientChallenge), nil +} + +func (r *scramsha256InitialRequest) numArg() int { + return 1 +} + +func (r *scramsha256InitialRequest) write(wr *bufio.Writer) error { + if err := wr.WriteInt16(3); err != nil { //field count + return err + } + if err := writeAuthField(wr, r.username); err != nil { + return err + } + if err := writeAuthField(wr, []byte(mnSCRAMSHA256)); err != nil { + return err + } + if err := writeAuthField(wr, r.clientChallenge); err != nil { + return err + } + return nil +} + +type scramsha256InitialReply struct { + salt []byte + serverChallenge []byte +} + +func newScramsha256InitialReply() *scramsha256InitialReply { + return &scramsha256InitialReply{} +} + +func (r *scramsha256InitialReply) kind() partKind { + return pkAuthentication +} + +func (r *scramsha256InitialReply) setNumArg(int) { + //not needed +} + +func (r *scramsha256InitialReply) read(rd *bufio.Reader) error { + cnt, err := rd.ReadInt16() + if err != nil { + return err + } + + if err := readMethodName(rd); err != nil { + return err + } + + size, err := rd.ReadByte() + if err != nil { + return err + } + if size != serverChallengeDataSize { + return fmt.Errorf("invalid server challenge data size %d - %d expected", size, serverChallengeDataSize) + } + + //server challenge data + + cnt, err = rd.ReadInt16() + if err != nil { + return err + } + if cnt != 2 { + return fmt.Errorf("invalid server challenge data field count %d - %d expected", cnt, 2) + } + + size, err = rd.ReadByte() + if err != nil { + return err + } + if trace { + outLogger.Printf("salt size %d", size) + } + + r.salt = make([]byte, size) + if err := rd.ReadFull(r.salt); err != nil { + return err + } + if trace { + outLogger.Printf("salt %v", r.salt) + } + + size, err = rd.ReadByte() + if err != nil { + return err + } + + r.serverChallenge = make([]byte, size) + if err := rd.ReadFull(r.serverChallenge); err != nil { + return err + } + if trace { + outLogger.Printf("server challenge %v", r.serverChallenge) + } + + return nil +} + +type scramsha256FinalRequest struct { + username []byte + clientProof []byte +} + +func newScramsha256FinalRequest() *scramsha256FinalRequest { + return &scramsha256FinalRequest{} +} + +func (r *scramsha256FinalRequest) kind() partKind { + return pkAuthentication +} + +func (r *scramsha256FinalRequest) size() (int, error) { + return 2 + authFieldSize(r.username) + authFieldSize([]byte(mnSCRAMSHA256)) + authFieldSize(r.clientProof), nil +} + +func (r *scramsha256FinalRequest) numArg() int { + return 1 +} + +func (r *scramsha256FinalRequest) write(wr *bufio.Writer) error { + if err := wr.WriteInt16(3); err != nil { //field count + return err + } + if err := writeAuthField(wr, r.username); err != nil { + return err + } + if err := writeAuthField(wr, []byte(mnSCRAMSHA256)); err != nil { + return err + } + if err := writeAuthField(wr, r.clientProof); err != nil { + return err + } + return nil +} + +type scramsha256FinalReply struct { + serverProof []byte +} + +func newScramsha256FinalReply() *scramsha256FinalReply { + return &scramsha256FinalReply{} +} + +func (r *scramsha256FinalReply) kind() partKind { + return pkAuthentication +} + +func (r *scramsha256FinalReply) setNumArg(int) { + //not needed +} + +func (r *scramsha256FinalReply) read(rd *bufio.Reader) error { + cnt, err := rd.ReadInt16() + if err != nil { + return err + } + if cnt != 2 { + return fmt.Errorf("invalid final reply field count %d - %d expected", cnt, 2) + } + + if err := readMethodName(rd); err != nil { + return err + } + + //serverProof + size, err := rd.ReadByte() + if err != nil { + return err + } + + serverProof := make([]byte, size) + if err := rd.ReadFull(serverProof); err != nil { + return err + } + + return nil +} + +//helper +func authFieldSize(f []byte) int { + size := len(f) + if size >= 250 { + // - different indicators compared to db field handling + // - 1-5 bytes? but only 1 resp 3 bytes explained + panic("not implemented error") + } + return size + 1 //length indicator size := 1 +} + +func writeAuthField(wr *bufio.Writer, f []byte) error { + size := len(f) + if size >= 250 { + // - different indicators compared to db field handling + // - 1-5 bytes? but only 1 resp 3 bytes explained + panic("not implemented error") + } + + if err := wr.WriteByte(byte(size)); err != nil { + return err + } + + if _, err := wr.Write(f); err != nil { + return err + } + + return nil +} + +func readMethodName(rd *bufio.Reader) error { + size, err := rd.ReadByte() + if err != nil { + return err + } + methodName := make([]byte, size) + if err := rd.ReadFull(methodName); err != nil { + return err + } + if string(methodName) != mnSCRAMSHA256 { + return fmt.Errorf("invalid authentication method %s - %s expected", methodName, mnSCRAMSHA256) + } + return nil +} + +func clientChallenge() []byte { + r := make([]byte, clientChallengeSize) + if _, err := rand.Read(r); err != nil { + outLogger.Fatal("client challenge fatal error") + } + return r +} + +func clientProof(salt, serverChallenge, clientChallenge, password []byte) []byte { + + clientProof := make([]byte, clientProofDataSize) + + buf := make([]byte, 0, len(salt)+len(serverChallenge)+len(clientChallenge)) + buf = append(buf, salt...) + buf = append(buf, serverChallenge...) + buf = append(buf, clientChallenge...) + + key := _sha256(_hmac(password, salt)) + sig := _hmac(_sha256(key), buf) + + proof := xor(sig, key) + //actual implementation: only one salt value? + clientProof[0] = 0 + clientProof[1] = 1 + clientProof[2] = clientProofSize + copy(clientProof[3:], proof) + return clientProof +} + +func _sha256(p []byte) []byte { + hash := sha256.New() + hash.Write(p) + s := hash.Sum(nil) + if trace { + outLogger.Printf("sha length %d value %v", len(s), s) + } + return s +} + +func _hmac(key, p []byte) []byte { + hash := hmac.New(sha256.New, key) + hash.Write(p) + s := hash.Sum(nil) + if trace { + outLogger.Printf("hmac length %d value %v", len(s), s) + } + return s +} + +func xor(sig, key []byte) []byte { + r := make([]byte, len(sig)) + + for i, v := range sig { + r[i] = v ^ key[i] + } + return r +} diff --git a/vendor/github.com/SAP/go-hdb/internal/protocol/segment.go b/vendor/github.com/SAP/go-hdb/internal/protocol/segment.go new file mode 100644 index 000000000000..00d88044b982 --- /dev/null +++ b/vendor/github.com/SAP/go-hdb/internal/protocol/segment.go @@ -0,0 +1,238 @@ +/* +Copyright 2014 SAP SE + +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 protocol + +import ( + "fmt" + + "github.com/SAP/go-hdb/internal/bufio" +) + +const ( + segmentHeaderSize = 24 +) + +type commandOptions int8 + +const ( + coNil commandOptions = 0x00 + coSelfetchOff commandOptions = 0x01 + coScrollableCursorOn commandOptions = 0x02 + coNoResultsetCloseNeeded commandOptions = 0x04 + coHoldCursorOverCommtit commandOptions = 0x08 + coExecuteLocally commandOptions = 0x10 +) + +var commandOptionsText = map[commandOptions]string{ + coSelfetchOff: "selfetchOff", + coScrollableCursorOn: "scrollabeCursorOn", + coNoResultsetCloseNeeded: "noResltsetCloseNeeded", + coHoldCursorOverCommtit: "holdCursorOverCommit", + coExecuteLocally: "executLocally", +} + +func (k commandOptions) String() string { + t := make([]string, 0, len(commandOptionsText)) + + for option, text := range commandOptionsText { + if (k & option) != 0 { + t = append(t, text) + } + } + return fmt.Sprintf("%v", t) +} + +//segment header +type segmentHeader struct { + segmentLength int32 + segmentOfs int32 + noOfParts int16 + segmentNo int16 + segmentKind segmentKind + messageType messageType + commit bool + commandOptions commandOptions + functionCode functionCode +} + +func (h *segmentHeader) String() string { + switch h.segmentKind { + + default: //error + return fmt.Sprintf( + "segment length %d segment ofs %d noOfParts %d, segmentNo %d segmentKind %s", + h.segmentLength, + h.segmentOfs, + h.noOfParts, + h.segmentNo, + h.segmentKind, + ) + case skRequest: + return fmt.Sprintf( + "segment length %d segment ofs %d noOfParts %d, segmentNo %d segmentKind %s messageType %s commit %t commandOptions %s", + h.segmentLength, + h.segmentOfs, + h.noOfParts, + h.segmentNo, + h.segmentKind, + h.messageType, + h.commit, + h.commandOptions, + ) + case skReply: + return fmt.Sprintf( + "segment length %d segment ofs %d noOfParts %d, segmentNo %d segmentKind %s functionCode %s", + h.segmentLength, + h.segmentOfs, + h.noOfParts, + h.segmentNo, + h.segmentKind, + h.functionCode, + ) + } +} + +// request +func (h *segmentHeader) write(wr *bufio.Writer) error { + if err := wr.WriteInt32(h.segmentLength); err != nil { + return err + } + if err := wr.WriteInt32(h.segmentOfs); err != nil { + return err + } + if err := wr.WriteInt16(h.noOfParts); err != nil { + return err + } + if err := wr.WriteInt16(h.segmentNo); err != nil { + return err + } + if err := wr.WriteInt8(int8(h.segmentKind)); err != nil { + return err + } + + switch h.segmentKind { + + default: //error + if err := wr.WriteZeroes(11); err != nil { //segmentHeaderLength + return err + } + + case skRequest: + + if err := wr.WriteInt8(int8(h.messageType)); err != nil { + return err + } + if err := wr.WriteBool(h.commit); err != nil { + return err + } + if err := wr.WriteInt8(int8(h.commandOptions)); err != nil { + return err + } + + if err := wr.WriteZeroes(8); err != nil { //segmentHeaderSize + return err + } + + case skReply: + + if err := wr.WriteZeroes(1); err != nil { //reerved + return err + } + if err := wr.WriteInt16(int16(h.functionCode)); err != nil { + return err + } + + if err := wr.WriteZeroes(8); err != nil { //segmentHeaderSize + return err + } + + } + + if trace { + outLogger.Printf("write segment header: %s", h) + } + + return nil +} + +// reply || error +func (h *segmentHeader) read(rd *bufio.Reader) error { + var err error + + if h.segmentLength, err = rd.ReadInt32(); err != nil { + return err + } + if h.segmentOfs, err = rd.ReadInt32(); err != nil { + return err + } + if h.noOfParts, err = rd.ReadInt16(); err != nil { + return err + } + if h.segmentNo, err = rd.ReadInt16(); err != nil { + return err + } + if sk, err := rd.ReadInt8(); err == nil { + h.segmentKind = segmentKind(sk) + } else { + return err + } + + switch h.segmentKind { + + default: //error + if err := rd.Skip(11); err != nil { //segmentHeaderLength + return err + } + + case skRequest: + if mt, err := rd.ReadInt8(); err == nil { + h.messageType = messageType(mt) + } else { + return err + } + if h.commit, err = rd.ReadBool(); err != nil { + return err + } + if co, err := rd.ReadInt8(); err == nil { + h.commandOptions = commandOptions(co) + } else { + return err + } + if err := rd.Skip(8); err != nil { //segmentHeaderLength + return err + } + + case skReply: + if err := rd.Skip(1); err != nil { //reserved + return err + } + if fc, err := rd.ReadInt16(); err == nil { + h.functionCode = functionCode(fc) + } else { + return err + } + if err := rd.Skip(8); err != nil { //segmentHeaderLength + return err + } + } + + if trace { + outLogger.Printf("read segment header: %s", h) + } + + return nil +} diff --git a/vendor/github.com/SAP/go-hdb/internal/protocol/segmentkind.go b/vendor/github.com/SAP/go-hdb/internal/protocol/segmentkind.go new file mode 100644 index 000000000000..14151b6d429f --- /dev/null +++ b/vendor/github.com/SAP/go-hdb/internal/protocol/segmentkind.go @@ -0,0 +1,28 @@ +/* +Copyright 2014 SAP SE + +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 protocol + +//go:generate stringer -type=segmentKind + +type segmentKind int8 + +const ( + skInvalid segmentKind = 0 + skRequest segmentKind = 1 + skReply segmentKind = 2 + skError segmentKind = 5 +) diff --git a/vendor/github.com/SAP/go-hdb/internal/protocol/segmentkind_string.go b/vendor/github.com/SAP/go-hdb/internal/protocol/segmentkind_string.go new file mode 100644 index 000000000000..98f5f943a8f5 --- /dev/null +++ b/vendor/github.com/SAP/go-hdb/internal/protocol/segmentkind_string.go @@ -0,0 +1,26 @@ +// generated by stringer -type=segmentKind; DO NOT EDIT + +package protocol + +import "fmt" + +const ( + _segmentKind_name_0 = "skInvalidskRequestskReply" + _segmentKind_name_1 = "skError" +) + +var ( + _segmentKind_index_0 = [...]uint8{0, 9, 18, 25} + _segmentKind_index_1 = [...]uint8{0, 7} +) + +func (i segmentKind) String() string { + switch { + case 0 <= i && i <= 2: + return _segmentKind_name_0[_segmentKind_index_0[i]:_segmentKind_index_0[i+1]] + case i == 5: + return _segmentKind_name_1 + default: + return fmt.Sprintf("segmentKind(%d)", i) + } +} diff --git a/vendor/github.com/SAP/go-hdb/internal/protocol/session.go b/vendor/github.com/SAP/go-hdb/internal/protocol/session.go new file mode 100644 index 000000000000..9bcaef027c9e --- /dev/null +++ b/vendor/github.com/SAP/go-hdb/internal/protocol/session.go @@ -0,0 +1,986 @@ +/* +Copyright 2014 SAP SE + +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 protocol + +import ( + "database/sql/driver" + "flag" + "fmt" + "log" + "math" + "net" + "os" + "time" + + "github.com/SAP/go-hdb/internal/bufio" + "github.com/SAP/go-hdb/internal/unicode" + "github.com/SAP/go-hdb/internal/unicode/cesu8" + + "github.com/SAP/go-hdb/driver/sqltrace" +) + +const ( + mnSCRAMSHA256 = "SCRAMSHA256" + mnGSS = "GSS" + mnSAML = "SAML" +) + +var trace bool + +func init() { + flag.BoolVar(&trace, "hdb.protocol.trace", false, "enabling hdb protocol trace") +} + +var ( + outLogger = log.New(os.Stdout, "hdb.protocol ", log.Ldate|log.Ltime|log.Lshortfile) + errLogger = log.New(os.Stderr, "hdb.protocol ", log.Ldate|log.Ltime|log.Lshortfile) +) + +//padding +const ( + padding = 8 +) + +func padBytes(size int) int { + if r := size % padding; r != 0 { + return padding - r + } + return 0 +} + +// SessionConn wraps the database tcp connection. It sets timeouts and handles driver ErrBadConn behavior. +type sessionConn struct { + addr string + timeoutDuration time.Duration + conn net.Conn + isBad bool // bad connection + badError error // error cause for session bad state + inTx bool // in transaction +} + +func newSessionConn(addr string, timeout int) (*sessionConn, error) { + timeoutDuration := time.Duration(timeout) * time.Second + conn, err := net.DialTimeout("tcp", addr, timeoutDuration) + if err != nil { + return nil, err + } + + return &sessionConn{ + addr: addr, + timeoutDuration: timeoutDuration, + conn: conn, + }, nil +} + +func (c *sessionConn) close() error { + return c.conn.Close() +} + +// Read implements the io.Reader interface. +func (c *sessionConn) Read(b []byte) (int, error) { + //set timeout + if err := c.conn.SetReadDeadline(time.Now().Add(c.timeoutDuration)); err != nil { + return 0, err + } + n, err := c.conn.Read(b) + if err != nil { + errLogger.Printf("Connection read error local address %s remote address %s: %s", c.conn.LocalAddr(), c.conn.RemoteAddr(), err) + c.isBad = true + c.badError = err + return n, driver.ErrBadConn + } + return n, nil +} + +// Write implements the io.Writer interface. +func (c *sessionConn) Write(b []byte) (int, error) { + //set timeout + if err := c.conn.SetWriteDeadline(time.Now().Add(c.timeoutDuration)); err != nil { + return 0, err + } + n, err := c.conn.Write(b) + if err != nil { + errLogger.Printf("Connection write error local address %s remote address %s: %s", c.conn.LocalAddr(), c.conn.RemoteAddr(), err) + c.isBad = true + c.badError = err + return n, driver.ErrBadConn + } + return n, nil +} + +type providePart func(pk partKind) replyPart +type beforeRead func(p replyPart) + +// Session represents a HDB session. +type Session struct { + prm *SessionPrm + + conn *sessionConn + rd *bufio.Reader + wr *bufio.Writer + + // reuse header + mh *messageHeader + sh *segmentHeader + ph *partHeader + + //reuse request / reply parts + rowsAffected *rowsAffected + statementID *statementID + resultMetadata *resultMetadata + resultsetID *resultsetID + resultset *resultset + parameterMetadata *parameterMetadata + outputParameters *outputParameters + readLobRequest *readLobRequest + readLobReply *readLobReply + + //standard replies + stmtCtx *statementContext + txFlags *transactionFlags + lastError *hdbError +} + +// NewSession creates a new database session. +func NewSession(prm *SessionPrm) (*Session, error) { + + if trace { + outLogger.Printf("%s", prm) + } + + conn, err := newSessionConn(prm.Host, prm.Timeout) + if err != nil { + return nil, err + } + + var rd *bufio.Reader + var wr *bufio.Writer + if prm.BufferSize > 0 { + rd = bufio.NewReaderSize(conn, prm.BufferSize) + wr = bufio.NewWriterSize(conn, prm.BufferSize) + } else { + rd = bufio.NewReader(conn) + wr = bufio.NewWriter(conn) + } + + s := &Session{ + prm: prm, + conn: conn, + rd: rd, + wr: wr, + mh: new(messageHeader), + sh: new(segmentHeader), + ph: new(partHeader), + rowsAffected: new(rowsAffected), + statementID: new(statementID), + resultMetadata: new(resultMetadata), + resultsetID: new(resultsetID), + resultset: new(resultset), + parameterMetadata: new(parameterMetadata), + outputParameters: new(outputParameters), + readLobRequest: new(readLobRequest), + readLobReply: new(readLobReply), + stmtCtx: newStatementContext(), + txFlags: newTransactionFlags(), + lastError: newHdbError(), + } + + if err = s.init(); err != nil { + return nil, err + } + + return s, nil +} + +// Close closes the session. +func (s *Session) Close() error { + return s.conn.close() +} + +func (s *Session) sessionID() int64 { + return s.mh.sessionID +} + +// InTx indicates, if the session is in transaction mode. +func (s *Session) InTx() bool { + return s.conn.inTx +} + +// SetInTx sets session in transaction mode. +func (s *Session) SetInTx(v bool) { + s.conn.inTx = v +} + +// IsBad indicates, that the session is in bad state. +func (s *Session) IsBad() bool { + return s.conn.isBad +} + +// BadErr returns the error, that caused the bad session state. +func (s *Session) BadErr() error { + return s.conn.badError +} + +func (s *Session) init() error { + + if err := s.initRequest(); err != nil { + return err + } + + // TODO: detect authentication method + // - actually only basic authetication supported + + authentication := mnSCRAMSHA256 + + switch authentication { + default: + return fmt.Errorf("invalid authentication %s", authentication) + + case mnSCRAMSHA256: + if err := s.authenticateScramsha256(); err != nil { + return err + } + case mnGSS: + panic("not implemented error") + case mnSAML: + panic("not implemented error") + } + + id := s.sessionID() + if id <= 0 { + return fmt.Errorf("invalid session id %d", id) + } + + if trace { + outLogger.Printf("sessionId %d", id) + } + + return nil +} + +func (s *Session) authenticateScramsha256() error { + tr := unicode.Utf8ToCesu8Transformer + tr.Reset() + + username := make([]byte, cesu8.StringSize(s.prm.Username)) + if _, _, err := tr.Transform(username, []byte(s.prm.Username), true); err != nil { + return err // should never happen + } + + password := make([]byte, cesu8.StringSize(s.prm.Password)) + if _, _, err := tr.Transform(password, []byte(s.prm.Password), true); err != nil { + return err //should never happen + } + + clientChallenge := clientChallenge() + + //initial request + ireq := newScramsha256InitialRequest() + ireq.username = username + ireq.clientChallenge = clientChallenge + + if err := s.writeRequest(mtAuthenticate, false, ireq); err != nil { + return err + } + + irep := newScramsha256InitialReply() + + f := func(pk partKind) replyPart { + switch pk { + case pkAuthentication: + return irep + default: + return nil + } + } + + if err := s.readReply(f, nil); err != nil { + return err + } + + //final request + freq := newScramsha256FinalRequest() + freq.username = username + freq.clientProof = clientProof(irep.salt, irep.serverChallenge, clientChallenge, password) + + id := newClientID() + + co := newConnectOptions() + co.set(coDistributionProtocolVersion, booleanType(false)) + co.set(coSelectForUpdateSupported, booleanType(false)) + co.set(coSplitBatchCommands, booleanType(true)) + co.set(coDataFormatVersion, dfvBaseline) + co.set(coDataFormatVersion2, dfvBaseline) + co.set(coCompleteArrayExecution, booleanType(true)) + co.set(coClientLocale, stringType(s.prm.Locale)) + co.set(coClientDistributionMode, cdmOff) + + if err := s.writeRequest(mtConnect, false, freq, id, co); err != nil { + return err + } + + frep := newScramsha256FinalReply() + topo := newTopologyOptions() + + f = func(pk partKind) replyPart { + switch pk { + case pkAuthentication: + return frep + case pkTopologyInformation: + return topo + case pkConnectOptions: + return co + default: + return nil + } + } + + if err := s.readReply(f, nil); err != nil { + return err + } + + return nil +} + +// QueryDirect executes a query without query parameters. +func (s *Session) QueryDirect(query string) (uint64, *FieldSet, *FieldValues, PartAttributes, error) { + + if err := s.writeRequest(mtExecuteDirect, false, command(query)); err != nil { + return 0, nil, nil, nil, err + } + + var id uint64 + var fieldSet *FieldSet + fieldValues := newFieldValues(s) + + f := func(p replyPart) { + + switch p := p.(type) { + + case *resultsetID: + p.id = &id + case *resultMetadata: + fieldSet = newFieldSet(p.numArg) + p.fieldSet = fieldSet + case *resultset: + p.fieldSet = fieldSet + p.fieldValues = fieldValues + } + } + + if err := s.readReply(nil, f); err != nil { + return 0, nil, nil, nil, err + } + + attrs := s.ph.partAttributes + + return id, fieldSet, fieldValues, attrs, nil +} + +// ExecDirect executes a sql statement without statement parameters. +func (s *Session) ExecDirect(query string) (driver.Result, error) { + + if err := s.writeRequest(mtExecuteDirect, !s.conn.inTx, command(query)); err != nil { + return nil, err + } + + if err := s.readReply(nil, nil); err != nil { + return nil, err + } + + if s.sh.functionCode == fcDDL { + return driver.ResultNoRows, nil + } + return driver.RowsAffected(s.rowsAffected.total()), nil +} + +// Prepare prepares a sql statement. +func (s *Session) Prepare(query string) (QueryType, uint64, *FieldSet, *FieldSet, error) { + + if err := s.writeRequest(mtPrepare, false, command(query)); err != nil { + return QtNone, 0, nil, nil, err + } + + var id uint64 + var prmFieldSet *FieldSet + var resultFieldSet *FieldSet + + f := func(p replyPart) { + + switch p := p.(type) { + + case *statementID: + p.id = &id + case *parameterMetadata: + prmFieldSet = newFieldSet(p.numArg) + p.fieldSet = prmFieldSet + case *resultMetadata: + resultFieldSet = newFieldSet(p.numArg) + p.fieldSet = resultFieldSet + } + } + + if err := s.readReply(nil, f); err != nil { + return QtNone, 0, nil, nil, err + } + + return s.sh.functionCode.queryType(), id, prmFieldSet, resultFieldSet, nil +} + +// Exec executes a sql statement. +func (s *Session) Exec(id uint64, parameterFieldSet *FieldSet, args []driver.Value) (driver.Result, error) { + + s.statementID.id = &id + if err := s.writeRequest(mtExecute, !s.conn.inTx, s.statementID, newParameters(parameterFieldSet, args)); err != nil { + return nil, err + } + + wlr := newWriteLobReply() //lob streaming + + f := func(pk partKind) replyPart { + switch pk { + case pkWriteLobReply: + return wlr + default: + return nil + } + } + + if err := s.readReply(f, nil); err != nil { + return nil, err + } + + var result driver.Result + if s.sh.functionCode == fcDDL { + result = driver.ResultNoRows + } else { + result = driver.RowsAffected(s.rowsAffected.total()) + } + + if wlr.numArg > 0 { + if err := s.writeLobStream(parameterFieldSet, nil, args, wlr); err != nil { + return nil, err + } + } + + return result, nil +} + +// DropStatementID releases the hdb statement handle. +func (s *Session) DropStatementID(id uint64) error { + + s.statementID.id = &id + if err := s.writeRequest(mtDropStatementID, false, s.statementID); err != nil { + return err + } + + if err := s.readReply(nil, nil); err != nil { + return err + } + + return nil +} + +// Call executes a stored procedure. +func (s *Session) Call(id uint64, prmFieldSet *FieldSet, args []driver.Value) (*FieldValues, []*TableResult, error) { + + s.statementID.id = &id + if err := s.writeRequest(mtExecute, false, s.statementID, newParameters(prmFieldSet, args)); err != nil { + return nil, nil, err + } + + wlr := newWriteLobReply() //lob streaming + + f := func(pk partKind) replyPart { + switch pk { + case pkWriteLobReply: + return wlr + default: + return nil + } + } + + prmFieldValues := newFieldValues(s) + var tableResults []*TableResult + var tableResult *TableResult + + g := func(p replyPart) { + + switch p := p.(type) { + + case *outputParameters: + p.fieldSet = prmFieldSet + p.fieldValues = prmFieldValues + + // table output parameters: meta, id, result (only first param?) + case *resultMetadata: + tableResult = newTableResult(s, p.numArg) + tableResults = append(tableResults, tableResult) + p.fieldSet = tableResult.fieldSet + case *resultsetID: + p.id = &(tableResult.id) + case *resultset: + tableResult.attrs = s.ph.partAttributes + p.fieldSet = tableResult.fieldSet + p.fieldValues = tableResult.fieldValues + } + } + + if err := s.readReply(f, g); err != nil { + return nil, nil, err + } + + if wlr.numArg > 0 { + if err := s.writeLobStream(prmFieldSet, prmFieldValues, args, wlr); err != nil { + return nil, nil, err + } + } + + return prmFieldValues, tableResults, nil +} + +// Query executes a query. +func (s *Session) Query(stmtID uint64, parameterFieldSet *FieldSet, resultFieldSet *FieldSet, args []driver.Value) (uint64, *FieldValues, PartAttributes, error) { + + s.statementID.id = &stmtID + if err := s.writeRequest(mtExecute, false, s.statementID, newParameters(parameterFieldSet, args)); err != nil { + return 0, nil, nil, err + } + + var rsetID uint64 + fieldValues := newFieldValues(s) + + f := func(p replyPart) { + + switch p := p.(type) { + + case *resultsetID: + p.id = &rsetID + case *resultset: + p.fieldSet = resultFieldSet + p.fieldValues = fieldValues + } + } + + if err := s.readReply(nil, f); err != nil { + return 0, nil, nil, err + } + + attrs := s.ph.partAttributes + + return rsetID, fieldValues, attrs, nil +} + +// FetchNext fetches next chunk in query result set. +func (s *Session) FetchNext(id uint64, resultFieldSet *FieldSet) (*FieldValues, PartAttributes, error) { + s.resultsetID.id = &id + if err := s.writeRequest(mtFetchNext, false, s.resultsetID, fetchsize(s.prm.FetchSize)); err != nil { + return nil, nil, err + } + + fieldValues := newFieldValues(s) + + f := func(p replyPart) { + + switch p := p.(type) { + + case *resultset: + p.fieldSet = resultFieldSet + p.fieldValues = fieldValues + } + } + + if err := s.readReply(nil, f); err != nil { + return nil, nil, err + } + + attrs := s.ph.partAttributes + + return fieldValues, attrs, nil +} + +// CloseResultsetID releases the hdb resultset handle. +func (s *Session) CloseResultsetID(id uint64) error { + + s.resultsetID.id = &id + if err := s.writeRequest(mtCloseResultset, false, s.resultsetID); err != nil { + return err + } + + if err := s.readReply(nil, nil); err != nil { + return err + } + + return nil +} + +// Commit executes a database commit. +func (s *Session) Commit() error { + + if err := s.writeRequest(mtCommit, false); err != nil { + return err + } + + if err := s.readReply(nil, nil); err != nil { + return err + } + + if trace { + outLogger.Printf("transaction flags: %s", s.txFlags) + } + + s.conn.inTx = false + return nil +} + +// Rollback executes a database rollback. +func (s *Session) Rollback() error { + + if err := s.writeRequest(mtRollback, false); err != nil { + return err + } + + if err := s.readReply(nil, nil); err != nil { + return err + } + + if trace { + outLogger.Printf("transaction flags: %s", s.txFlags) + } + + s.conn.inTx = false + return nil +} + +// helper +func readLobStreamDone(writers []lobWriter) bool { + for _, writer := range writers { + if !writer.eof() { + return false + } + } + return true +} + +// + +func (s *Session) readLobStream(writers []lobWriter) error { + + f := func(pk partKind) replyPart { + switch pk { + case pkReadLobReply: + return s.readLobReply + default: + return nil + } + } + + for !readLobStreamDone(writers) { + + s.readLobRequest.writers = writers + s.readLobReply.writers = writers + + if err := s.writeRequest(mtWriteLob, false, s.readLobRequest); err != nil { + return err + } + if err := s.readReply(f, nil); err != nil { + return err + } + } + return nil +} + +func (s *Session) writeLobStream(prmFieldSet *FieldSet, prmFieldValues *FieldValues, args []driver.Value, reply *writeLobReply) error { + + num := reply.numArg + readers := make([]lobReader, num) + + request := newWriteLobRequest(readers) + + j := 0 + for i, field := range prmFieldSet.fields { + + if field.typeCode().isLob() && field.in() { + + ptr, ok := args[i].(int64) + if !ok { + return fmt.Errorf("protocol error: invalid lob driver value type %T", args[i]) + } + + descr := pointerToLobWriteDescr(ptr) + if descr.r == nil { + return fmt.Errorf("protocol error: lob reader %d initial", ptr) + } + + if j >= num { + return fmt.Errorf("protocol error: invalid number of lob parameter ids %d", num) + } + + if field.typeCode().isCharBased() { + readers[j] = newCharLobReader(descr.r, reply.ids[j]) + } else { + readers[j] = newBinaryLobReader(descr.r, reply.ids[j]) + } + + j++ + } + } + + f := func(pk partKind) replyPart { + switch pk { + case pkWriteLobReply: + return reply + default: + return nil + } + } + + g := func(p replyPart) { + if p, ok := p.(*outputParameters); ok { + p.fieldSet = prmFieldSet + p.fieldValues = prmFieldValues + } + } + + for reply.numArg != 0 { + if err := s.writeRequest(mtReadLob, false, request); err != nil { + return err + } + + if err := s.readReply(f, g); err != nil { + return err + } + } + + return nil +} + +// + +func (s *Session) initRequest() error { + + // init + s.mh.sessionID = -1 + + // handshake + req := newInitRequest() + // TODO: constants + req.product.major = 4 + req.product.minor = 20 + req.protocol.major = 4 + req.protocol.minor = 1 + req.numOptions = 1 + req.endianess = archEndian + if err := req.write(s.wr); err != nil { + return err + } + + rep := newInitReply() + if err := rep.read(s.rd); err != nil { + return err + } + return nil +} + +func (s *Session) writeRequest(messageType messageType, commit bool, requests ...requestPart) error { + + partSize := make([]int, len(requests)) + + size := int64(segmentHeaderSize + len(requests)*partHeaderSize) //int64 to hold MaxUInt32 in 32bit OS + + for i, part := range requests { + s, err := part.size() + if err != nil { + return err + } + size += int64(s + padBytes(s)) + partSize[i] = s // buffer size (expensive calculation) + } + + if size > math.MaxUint32 { + return fmt.Errorf("message size %d exceeds maximum message header value %d", size, int64(math.MaxUint32)) //int64: without cast overflow error in 32bit OS + } + + bufferSize := size + + s.mh.varPartLength = uint32(size) + s.mh.varPartSize = uint32(bufferSize) + s.mh.noOfSegm = 1 + + if err := s.mh.write(s.wr); err != nil { + return err + } + + if size > math.MaxInt32 { + return fmt.Errorf("message size %d exceeds maximum part header value %d", size, math.MaxInt32) + } + + s.sh.messageType = messageType + s.sh.commit = commit + s.sh.segmentKind = skRequest + s.sh.segmentLength = int32(size) + s.sh.segmentOfs = 0 + s.sh.noOfParts = int16(len(requests)) + s.sh.segmentNo = 1 + + if err := s.sh.write(s.wr); err != nil { + return err + } + + bufferSize -= segmentHeaderSize + + for i, part := range requests { + + size := partSize[i] + pad := padBytes(size) + + s.ph.partKind = part.kind() + numArg := part.numArg() + switch { + default: + return fmt.Errorf("maximum number of arguments %d exceeded", numArg) + case numArg <= math.MaxInt16: + s.ph.argumentCount = int16(numArg) + s.ph.bigArgumentCount = 0 + + // TODO: seems not to work: see bulk insert test + case numArg <= math.MaxInt32: + s.ph.argumentCount = 0 + s.ph.bigArgumentCount = int32(numArg) + } + + s.ph.bufferLength = int32(size) + s.ph.bufferSize = int32(bufferSize) + + if err := s.ph.write(s.wr); err != nil { + return err + } + + if err := part.write(s.wr); err != nil { + return err + } + + if err := s.wr.WriteZeroes(pad); err != nil { + return err + } + + bufferSize -= int64(partHeaderSize + size + pad) + + } + + if err := s.wr.Flush(); err != nil { + return err + } + + return nil +} + +func (s *Session) readReply(providePart providePart, beforeRead beforeRead) error { + + replyError := false + + if err := s.mh.read(s.rd); err != nil { + return err + } + if s.mh.noOfSegm != 1 { + return fmt.Errorf("simple message: no of segments %d - expected 1", s.mh.noOfSegm) + } + if err := s.sh.read(s.rd); err != nil { + return err + } + + // TODO: protocol error (sps 82)?: message header varPartLength < segment header segmentLength (*1) + diff := int(s.mh.varPartLength) - int(s.sh.segmentLength) + if trace && diff != 0 { + outLogger.Printf("+++++diff %d", diff) + } + + noOfParts := int(s.sh.noOfParts) + + for i := 0; i < noOfParts; i++ { + + if err := s.ph.read(s.rd); err != nil { + return err + } + + numArg := int(s.ph.argumentCount) + + var part replyPart + + if providePart != nil { + part = providePart(s.ph.partKind) + } else { + part = nil + } + + if part == nil { // use pre defined parts + + switch s.ph.partKind { + + case pkStatementID: + part = s.statementID + case pkResultMetadata: + part = s.resultMetadata + case pkResultsetID: + part = s.resultsetID + case pkResultset: + part = s.resultset + case pkParameterMetadata: + part = s.parameterMetadata + case pkOutputParameters: + part = s.outputParameters + case pkError: + replyError = true + part = s.lastError + case pkStatementContext: + part = s.stmtCtx + case pkTransactionFlags: + part = s.txFlags + case pkRowsAffected: + part = s.rowsAffected + default: + return fmt.Errorf("read not expected part kind %s", s.ph.partKind) + } + } + + part.setNumArg(numArg) + + if beforeRead != nil { + beforeRead(part) + } + + if err := part.read(s.rd); err != nil { + return err + } + + // TODO: workaround (see *) + if i != (noOfParts-1) || (i == (noOfParts-1) && diff == 0) { + if err := s.rd.Skip(padBytes(int(s.ph.bufferLength))); err != nil { + return err + } + } + } + + if replyError { + if s.lastError.IsWarning() { + sqltrace.Traceln(s.lastError) + } else { + return s.lastError + } + } + return nil +} diff --git a/vendor/github.com/SAP/go-hdb/internal/protocol/sessionprm.go b/vendor/github.com/SAP/go-hdb/internal/protocol/sessionprm.go new file mode 100644 index 000000000000..ee4075690935 --- /dev/null +++ b/vendor/github.com/SAP/go-hdb/internal/protocol/sessionprm.go @@ -0,0 +1,29 @@ +/* +Copyright 2014 SAP SE + +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 protocol + +import "fmt" + +type SessionPrm struct { + Host, Username, Password string + Locale string + BufferSize, FetchSize, Timeout int +} + +func (p *SessionPrm) String() string { + return fmt.Sprintf("session parameters: bufferSize %d fetchSize %d timeout %d locale %s", p.BufferSize, p.FetchSize, p.Timeout, p.Locale) +} diff --git a/vendor/github.com/SAP/go-hdb/internal/protocol/sniffer.go b/vendor/github.com/SAP/go-hdb/internal/protocol/sniffer.go new file mode 100644 index 000000000000..ba638c29019f --- /dev/null +++ b/vendor/github.com/SAP/go-hdb/internal/protocol/sniffer.go @@ -0,0 +1,207 @@ +/* +Copyright 2014 SAP SE + +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 protocol + +import ( + "log" + "net" + + "github.com/SAP/go-hdb/internal/bufio" +) + +type dir bool + +const ( + maxBinarySize = 128 +) + +type fragment interface { + read(rd *bufio.Reader) error + write(wr *bufio.Writer) error +} + +func (d dir) String() string { + if d { + return "->" + } + return "<-" +} + +// A Sniffer is a simple proxy for logging hdb protocol requests and responses. +type Sniffer struct { + conn net.Conn + dbAddr string + dbConn net.Conn + + //client + clRd *bufio.Reader + clWr *bufio.Writer + //database + dbRd *bufio.Reader + dbWr *bufio.Writer + + mh *messageHeader + sh *segmentHeader + ph *partHeader + + buf []byte +} + +// NewSniffer creates a new sniffer instance. The conn parameter is the net.Conn connection, where the Sniffer +// is listening for hdb protocol calls. The dbAddr is the hdb host port address in "host:port" format. +func NewSniffer(conn net.Conn, dbAddr string) (*Sniffer, error) { + s := &Sniffer{ + conn: conn, + dbAddr: dbAddr, + clRd: bufio.NewReader(conn), + clWr: bufio.NewWriter(conn), + mh: &messageHeader{}, + sh: &segmentHeader{}, + ph: &partHeader{}, + buf: make([]byte, 0), + } + + dbConn, err := net.Dial("tcp", s.dbAddr) + if err != nil { + return nil, err + } + + s.dbRd = bufio.NewReader(dbConn) + s.dbWr = bufio.NewWriter(dbConn) + s.dbConn = dbConn + return s, nil +} + +func (s *Sniffer) getBuffer(size int) []byte { + if cap(s.buf) < size { + s.buf = make([]byte, size) + } + return s.buf[:size] +} + +// Go starts the protocol request and response logging. +func (s *Sniffer) Go() { + defer s.dbConn.Close() + defer s.conn.Close() + + req := newInitRequest() + if err := s.streamFragment(dir(true), s.clRd, s.dbWr, req); err != nil { + return + } + + rep := newInitReply() + if err := s.streamFragment(dir(false), s.dbRd, s.clWr, rep); err != nil { + return + } + + for { + //up stream + if err := s.stream(dir(true), s.clRd, s.dbWr); err != nil { + return + } + //down stream + if err := s.stream(dir(false), s.dbRd, s.clWr); err != nil { + return + } + } +} + +func (s *Sniffer) stream(d dir, from *bufio.Reader, to *bufio.Writer) error { + + if err := s.streamFragment(d, from, to, s.mh); err != nil { + return err + } + + size := int(s.mh.varPartLength) + + for i := 0; i < int(s.mh.noOfSegm); i++ { + + if err := s.streamFragment(d, from, to, s.sh); err != nil { + return err + } + + size -= int(s.sh.segmentLength) + + for j := 0; j < int(s.sh.noOfParts); j++ { + + if err := s.streamFragment(d, from, to, s.ph); err != nil { + return err + } + + // protocol error workaraound + padding := (size == 0) || (j != (int(s.sh.noOfParts) - 1)) + + if err := s.streamPart(d, from, to, s.ph, padding); err != nil { + return err + } + } + } + + to.Flush() + + return nil +} + +func (s *Sniffer) streamPart(d dir, from *bufio.Reader, to *bufio.Writer, ph *partHeader, padding bool) error { + + switch ph.partKind { + + default: + return s.streamBinary(d, from, to, int(ph.bufferLength), padding) + } +} + +func (s *Sniffer) streamBinary(d dir, from *bufio.Reader, to *bufio.Writer, size int, padding bool) error { + var b []byte + + //protocol error workaraound + if padding { + pad := padBytes(size) + b = s.getBuffer(size + pad) + } else { + b = s.getBuffer(size) + } + + if err := from.ReadFull(b); err != nil { + log.Print(err) + return err + } + + if size > maxBinarySize { + log.Printf("%s %v", d, b[:maxBinarySize]) + } else { + log.Printf("%s %v", d, b[:size]) + } + if _, err := to.Write(b); err != nil { + log.Print(err) + return err + } + return nil +} + +func (s *Sniffer) streamFragment(d dir, from *bufio.Reader, to *bufio.Writer, f fragment) error { + if err := f.read(from); err != nil { + log.Print(err) + return err + } + log.Printf("%s %s", d, f) + if err := f.write(to); err != nil { + log.Print(err) + return err + } + return nil +} diff --git a/vendor/github.com/SAP/go-hdb/internal/protocol/statementcontext.go b/vendor/github.com/SAP/go-hdb/internal/protocol/statementcontext.go new file mode 100644 index 000000000000..d4d4d3c77ff5 --- /dev/null +++ b/vendor/github.com/SAP/go-hdb/internal/protocol/statementcontext.go @@ -0,0 +1,62 @@ +/* +Copyright 2014 SAP SE + +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 protocol + +import ( + "fmt" + + "github.com/SAP/go-hdb/internal/bufio" +) + +type statementContext struct { + options plainOptions + _numArg int +} + +func newStatementContext() *statementContext { + return &statementContext{ + options: plainOptions{}, + } +} + +func (c *statementContext) String() string { + typedSc := make(map[statementContextType]interface{}) + for k, v := range c.options { + typedSc[statementContextType(k)] = v + } + return fmt.Sprintf("%s", typedSc) +} + +func (c *statementContext) kind() partKind { + return pkStatementContext +} + +func (c *statementContext) setNumArg(numArg int) { + c._numArg = numArg +} + +func (c *statementContext) read(rd *bufio.Reader) error { + if err := c.options.read(rd, c._numArg); err != nil { + return err + } + + if trace { + outLogger.Printf("statement context: %v", c) + } + + return nil +} diff --git a/vendor/github.com/SAP/go-hdb/internal/protocol/statementcontexttype.go b/vendor/github.com/SAP/go-hdb/internal/protocol/statementcontexttype.go new file mode 100644 index 000000000000..7e9133b48aee --- /dev/null +++ b/vendor/github.com/SAP/go-hdb/internal/protocol/statementcontexttype.go @@ -0,0 +1,26 @@ +/* +Copyright 2014 SAP SE + +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 protocol + +//go:generate stringer -type=statementContextType + +type statementContextType int8 + +const ( + scStatementSequenceInfo statementContextType = 1 + scServerExecutionTime statementContextType = 2 +) diff --git a/vendor/github.com/SAP/go-hdb/internal/protocol/statementcontexttype_string.go b/vendor/github.com/SAP/go-hdb/internal/protocol/statementcontexttype_string.go new file mode 100644 index 000000000000..817e237f4e3d --- /dev/null +++ b/vendor/github.com/SAP/go-hdb/internal/protocol/statementcontexttype_string.go @@ -0,0 +1,17 @@ +// generated by stringer -type=statementContextType; DO NOT EDIT + +package protocol + +import "fmt" + +const _statementContextType_name = "scStatementSequenceInfoscServerExecutionTime" + +var _statementContextType_index = [...]uint8{0, 23, 44} + +func (i statementContextType) String() string { + i -= 1 + if i < 0 || i >= statementContextType(len(_statementContextType_index)-1) { + return fmt.Sprintf("statementContextType(%d)", i+1) + } + return _statementContextType_name[_statementContextType_index[i]:_statementContextType_index[i+1]] +} diff --git a/vendor/github.com/SAP/go-hdb/internal/protocol/statementid.go b/vendor/github.com/SAP/go-hdb/internal/protocol/statementid.go new file mode 100644 index 000000000000..9a40fae9e170 --- /dev/null +++ b/vendor/github.com/SAP/go-hdb/internal/protocol/statementid.go @@ -0,0 +1,73 @@ +/* +Copyright 2014 SAP SE + +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 protocol + +import ( + "github.com/SAP/go-hdb/internal/bufio" +) + +const ( + statementIDSize = 8 +) + +type statementID struct { + id *uint64 +} + +func (id statementID) kind() partKind { + return pkStatementID +} + +func (id statementID) size() (int, error) { + return statementIDSize, nil +} + +func (id statementID) numArg() int { + return 1 +} + +func (id statementID) setNumArg(int) { + //ignore - always 1 +} + +func (id *statementID) read(rd *bufio.Reader) error { + + _id, err := rd.ReadUint64() + if err != nil { + return err + } + *id.id = _id + + if trace { + outLogger.Printf("statement id: %d", *id.id) + } + + return nil +} + +func (id statementID) write(wr *bufio.Writer) error { + + if err := wr.WriteUint64(*id.id); err != nil { + return err + } + + if trace { + outLogger.Printf("statement id: %d", *id.id) + } + + return nil +} diff --git a/vendor/github.com/SAP/go-hdb/internal/protocol/tableresult.go b/vendor/github.com/SAP/go-hdb/internal/protocol/tableresult.go new file mode 100644 index 000000000000..f12c6d842f84 --- /dev/null +++ b/vendor/github.com/SAP/go-hdb/internal/protocol/tableresult.go @@ -0,0 +1,52 @@ +/* +Copyright 2014 SAP SE + +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 protocol + +// TableResult is the package internal representation of a table like output parameter of a stored procedure. +type TableResult struct { + id uint64 + fieldSet *FieldSet + fieldValues *FieldValues + attrs partAttributes +} + +func newTableResult(s *Session, size int) *TableResult { + return &TableResult{ + fieldSet: newFieldSet(size), + fieldValues: newFieldValues(s), + } +} + +// ID returns the resultset id. +func (r *TableResult) ID() uint64 { + return r.id +} + +// FieldSet returns the field metadata of the table. +func (r *TableResult) FieldSet() *FieldSet { + return r.fieldSet +} + +// FieldValues returns the field values (fetched resultset part) of the table. +func (r *TableResult) FieldValues() *FieldValues { + return r.fieldValues +} + +// Attrs returns the PartAttributes interface of the fetched resultset part. +func (r *TableResult) Attrs() PartAttributes { + return r.attrs +} diff --git a/vendor/github.com/SAP/go-hdb/internal/protocol/topology.go b/vendor/github.com/SAP/go-hdb/internal/protocol/topology.go new file mode 100644 index 000000000000..f0fa0290b703 --- /dev/null +++ b/vendor/github.com/SAP/go-hdb/internal/protocol/topology.go @@ -0,0 +1,92 @@ +/* +Copyright 2014 SAP SE + +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 protocol + +import ( + "fmt" + + "github.com/SAP/go-hdb/internal/bufio" +) + +type topologyOptions struct { + mlo multiLineOptions + _numArg int +} + +func newTopologyOptions() *topologyOptions { + return &topologyOptions{ + mlo: multiLineOptions{}, + } +} + +func (o *topologyOptions) String() string { + mlo := make([]map[topologyOption]interface{}, len(o.mlo)) + for i, po := range o.mlo { + typedPo := make(map[topologyOption]interface{}) + for k, v := range po { + typedPo[topologyOption(k)] = v + } + mlo[i] = typedPo + } + return fmt.Sprintf("%s", mlo) +} + +func (o *topologyOptions) kind() partKind { + return pkTopologyInformation +} + +func (o *topologyOptions) size() int { + return o.mlo.size() +} + +func (o *topologyOptions) numArg() int { + return len(o.mlo) +} + +func (o *topologyOptions) setNumArg(numArg int) { + o._numArg = numArg +} + +func (o *topologyOptions) read(rd *bufio.Reader) error { + if err := o.mlo.read(rd, o._numArg); err != nil { + return err + } + + if trace { + outLogger.Printf("topology options: %v", o) + } + + return nil +} + +func (o *topologyOptions) write(wr *bufio.Writer) error { + + for _, m := range o.mlo { + if err := wr.WriteInt16(int16(len(m))); err != nil { + return err + } + if err := o.mlo.write(wr); err != nil { + return err + } + } + + if trace { + outLogger.Printf("topology options: %v", o) + } + + return nil +} diff --git a/vendor/github.com/SAP/go-hdb/internal/protocol/topologyoption.go b/vendor/github.com/SAP/go-hdb/internal/protocol/topologyoption.go new file mode 100644 index 000000000000..e5de91e3de3c --- /dev/null +++ b/vendor/github.com/SAP/go-hdb/internal/protocol/topologyoption.go @@ -0,0 +1,36 @@ +/* +Copyright 2014 SAP SE + +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 protocol + +//go:generate stringer -type=topologyOption + +type topologyOption int8 + +const ( + toHostName topologyOption = 1 + toHostPortnumber topologyOption = 2 + toTenantName topologyOption = 3 + toLoadfactor topologyOption = 4 + toVolumeID topologyOption = 5 + toIsMaster topologyOption = 6 + toIsCurrentSession topologyOption = 7 + toServiceType topologyOption = 8 + toNetworkDomain topologyOption = 9 + toIsStandby topologyOption = 10 + toAllIPAddresses topologyOption = 11 + toAllHostNames topologyOption = 12 +) diff --git a/vendor/github.com/SAP/go-hdb/internal/protocol/topologyoption_string.go b/vendor/github.com/SAP/go-hdb/internal/protocol/topologyoption_string.go new file mode 100644 index 000000000000..a373a6a7aca2 --- /dev/null +++ b/vendor/github.com/SAP/go-hdb/internal/protocol/topologyoption_string.go @@ -0,0 +1,17 @@ +// generated by stringer -type=topologyOption; DO NOT EDIT + +package protocol + +import "fmt" + +const _topologyOption_name = "toHostNametoHostPortnumbertoTenantNametoLoadfactortoVolumeIDtoIsMastertoIsCurrentSessiontoServiceTypetoNetworkDomaintoIsStandbytoAllIPAddressestoAllHostNames" + +var _topologyOption_index = [...]uint8{0, 10, 26, 38, 50, 60, 70, 88, 101, 116, 127, 143, 157} + +func (i topologyOption) String() string { + i -= 1 + if i < 0 || i >= topologyOption(len(_topologyOption_index)-1) { + return fmt.Sprintf("topologyOption(%d)", i+1) + } + return _topologyOption_name[_topologyOption_index[i]:_topologyOption_index[i+1]] +} diff --git a/vendor/github.com/SAP/go-hdb/internal/protocol/transactionflags.go b/vendor/github.com/SAP/go-hdb/internal/protocol/transactionflags.go new file mode 100644 index 000000000000..ae5f5865b2f5 --- /dev/null +++ b/vendor/github.com/SAP/go-hdb/internal/protocol/transactionflags.go @@ -0,0 +1,62 @@ +/* +Copyright 2014 SAP SE + +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 protocol + +import ( + "fmt" + + "github.com/SAP/go-hdb/internal/bufio" +) + +type transactionFlags struct { + options plainOptions + _numArg int +} + +func newTransactionFlags() *transactionFlags { + return &transactionFlags{ + options: plainOptions{}, + } +} + +func (f *transactionFlags) String() string { + typedSc := make(map[transactionFlagType]interface{}) + for k, v := range f.options { + typedSc[transactionFlagType(k)] = v + } + return fmt.Sprintf("%s", typedSc) +} + +func (f *transactionFlags) kind() partKind { + return pkTransactionFlags +} + +func (f *transactionFlags) setNumArg(numArg int) { + f._numArg = numArg +} + +func (f *transactionFlags) read(rd *bufio.Reader) error { + if err := f.options.read(rd, f._numArg); err != nil { + return err + } + + if trace { + outLogger.Printf("transaction flags: %v", f) + } + + return nil +} diff --git a/vendor/github.com/SAP/go-hdb/internal/protocol/transactionflagtype.go b/vendor/github.com/SAP/go-hdb/internal/protocol/transactionflagtype.go new file mode 100644 index 000000000000..28ad7d3a4476 --- /dev/null +++ b/vendor/github.com/SAP/go-hdb/internal/protocol/transactionflagtype.go @@ -0,0 +1,32 @@ +/* +Copyright 2014 SAP SE + +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 protocol + +//go:generate stringer -type=transactionFlagType + +//transaction flags +type transactionFlagType int8 + +const ( + tfRolledback transactionFlagType = 0 + tfCommited transactionFlagType = 1 + tfNewIsolationLevel transactionFlagType = 2 + tfDDLCommitmodeChanged transactionFlagType = 3 + tfWriteTransactionStarted transactionFlagType = 4 + tfNowriteTransactionStarted transactionFlagType = 5 + tfSessionClosingTransactionError transactionFlagType = 6 +) diff --git a/vendor/github.com/SAP/go-hdb/internal/protocol/transactionflagtype_string.go b/vendor/github.com/SAP/go-hdb/internal/protocol/transactionflagtype_string.go new file mode 100644 index 000000000000..5b0dead27f4c --- /dev/null +++ b/vendor/github.com/SAP/go-hdb/internal/protocol/transactionflagtype_string.go @@ -0,0 +1,16 @@ +// generated by stringer -type=transactionFlagType; DO NOT EDIT + +package protocol + +import "fmt" + +const _transactionFlagType_name = "tfRolledbacktfCommitedtfNewIsolationLeveltfDDLCommitmodeChangedtfWriteTransactionStartedtfNowriteTransactionStartedtfSessionClosingTransactionError" + +var _transactionFlagType_index = [...]uint8{0, 12, 22, 41, 63, 88, 115, 147} + +func (i transactionFlagType) String() string { + if i < 0 || i >= transactionFlagType(len(_transactionFlagType_index)-1) { + return fmt.Sprintf("transactionFlagType(%d)", i) + } + return _transactionFlagType_name[_transactionFlagType_index[i]:_transactionFlagType_index[i+1]] +} diff --git a/vendor/github.com/SAP/go-hdb/internal/protocol/typecode.go b/vendor/github.com/SAP/go-hdb/internal/protocol/typecode.go new file mode 100644 index 000000000000..9f572ce474ab --- /dev/null +++ b/vendor/github.com/SAP/go-hdb/internal/protocol/typecode.go @@ -0,0 +1,138 @@ +/* +Copyright 2014 SAP SE + +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 protocol + +//go:generate stringer -type=typeCode + +// null value indicator is high bit +type typeCode byte + +const ( + tcNull typeCode = 0 + tcTinyint typeCode = 1 + tcSmallint typeCode = 2 + tcInt typeCode = 3 + tcBigint typeCode = 4 + tcDecimal typeCode = 5 + tcReal typeCode = 6 + tcDouble typeCode = 7 + tcChar typeCode = 8 + tcVarchar typeCode = 9 + tcNchar typeCode = 10 + tcNvarchar typeCode = 11 + tcBinary typeCode = 12 + tcVarbinary typeCode = 13 + // depricated with 3 (doku) - but table 'date' field uses it + tcDate typeCode = 14 + // depricated with 3 (doku) - but table 'time' field uses it + tcTime typeCode = 15 + // depricated with 3 (doku) - but table 'timestamp' field uses it + tcTimestamp typeCode = 16 + //tcTimetz typeCode = 17 // reserved: do not use + //tcTimeltz typeCode = 18 // reserved: do not use + //tcTimestamptz typeCode = 19 // reserved: do not use + //tcTimestampltz typeCode = 20 // reserved: do not use + //tcInvervalym typeCode = 21 // reserved: do not use + //tcInvervalds typeCode = 22 // reserved: do not use + //tcRowid typeCode = 23 // reserved: do not use + //tcUrowid typeCode = 24 // reserved: do not use + tcClob typeCode = 25 + tcNclob typeCode = 26 + tcBlob typeCode = 27 + tcBoolean typeCode = 28 + tcString typeCode = 29 + tcNstring typeCode = 30 + tcBlocator typeCode = 31 + tcNlocator typeCode = 32 + tcBstring typeCode = 33 + //tcDecimaldigitarray typeCode = 34 // reserved: do not use + tcVarchar2 typeCode = 35 + tcVarchar3 typeCode = 36 + tcNvarchar3 typeCode = 37 + tcVarbinary3 typeCode = 38 + //tcVargroup typeCode = 39 // reserved: do not use + //tcTinyintnotnull typeCode = 40 // reserved: do not use + //tcSmallintnotnull typeCode = 41 // reserved: do not use + //tcIntnotnull typeCode = 42 // reserved: do not use + //tcBigintnotnull typeCode = 43 // reserved: do not use + //tcArgument typeCode = 44 // reserved: do not use + //tcTable typeCode = 45 // reserved: do not use + //tcCursor typeCode = 46 // reserved: do not use + tcSmalldecimal typeCode = 47 + //tcAbapitab typeCode = 48 // not supported by GO hdb driver + //tcAbapstruct typeCode = 49 // not supported by GO hdb driver + //tcArray typeCode = 50 // reserved: do not use + tcText typeCode = 51 + tcShorttext typeCode = 52 + tcBintext typeCode = 53 + //tcFixedpointdecimal typeCode = 54 // reserved: do not use + tcAlphanum typeCode = 55 + //tcTlocator typeCode = 56 // reserved: do not use + tcLongdate typeCode = 61 + tcSeconddate typeCode = 62 + tcDaydate typeCode = 63 + tcSecondtime typeCode = 64 + //tcCsdate typeCode = 65 // reserved: do not use + //tcCstime typeCode = 66 // reserved: do not use + //tcBlobdisk typeCode = 71 // reserved: do not use + //tcClobdisk typeCode = 72 // reserved: do not use + //tcNclobdisk typeCode = 73 // reserved: do not use + tcGeometry typeCode = 74 + tcPoint typeCode = 75 + //tcFixed16 typeCode = 76 // reserved: do not use + //tcBlobhybrid typeCode = 77 // reserved: do not use + //tcClobhybrid typeCode = 78 // reserved: do not use + //tcNclobhybrid typeCode = 79 // reserved: do not use + tcPointz typeCode = 80 +) + +func (k typeCode) isLob() bool { + return k == tcClob || k == tcNclob || k == tcBlob +} + +func (k typeCode) isCharBased() bool { + return k == tcNvarchar || k == tcNstring || k == tcNclob +} + +func (k typeCode) dataType() DataType { + switch k { + default: + return DtUnknown + case tcTinyint: + return DtTinyint + case tcSmallint: + return DtSmallint + case tcInt: + return DtInt + case tcBigint: + return DtBigint + case tcReal: + return DtReal + case tcDouble: + return DtDouble + case tcDate, tcTime, tcTimestamp: + return DtTime + case tcDecimal: + return DtDecimal + case tcChar, tcVarchar, tcString, tcNchar, tcNvarchar, tcNstring: + return DtString + case tcBinary, tcVarbinary: + return DtBytes + case tcBlob, tcClob, tcNclob: + return DtLob + } +} diff --git a/vendor/github.com/SAP/go-hdb/internal/protocol/typecode_string.go b/vendor/github.com/SAP/go-hdb/internal/protocol/typecode_string.go new file mode 100644 index 000000000000..8f4bb85b3e25 --- /dev/null +++ b/vendor/github.com/SAP/go-hdb/internal/protocol/typecode_string.go @@ -0,0 +1,59 @@ +// generated by stringer -type=typeCode; DO NOT EDIT + +package protocol + +import "fmt" + +const ( + _typeCode_name_0 = "tcNulltcTinyinttcSmallinttcInttcBiginttcDecimaltcRealtcDoubletcChartcVarchartcNchartcNvarchartcBinarytcVarbinarytcDatetcTimetcTimestamp" + _typeCode_name_1 = "tcClobtcNclobtcBlobtcBooleantcStringtcNstringtcBlocatortcNlocatortcBstring" + _typeCode_name_2 = "tcVarchar2tcVarchar3tcNvarchar3tcVarbinary3" + _typeCode_name_3 = "tcSmalldecimal" + _typeCode_name_4 = "tcTexttcShorttexttcBintext" + _typeCode_name_5 = "tcAlphanum" + _typeCode_name_6 = "tcLongdatetcSeconddatetcDaydatetcSecondtime" + _typeCode_name_7 = "tcGeometrytcPoint" + _typeCode_name_8 = "tcPointz" +) + +var ( + _typeCode_index_0 = [...]uint8{0, 6, 15, 25, 30, 38, 47, 53, 61, 67, 76, 83, 93, 101, 112, 118, 124, 135} + _typeCode_index_1 = [...]uint8{0, 6, 13, 19, 28, 36, 45, 55, 65, 74} + _typeCode_index_2 = [...]uint8{0, 10, 20, 31, 43} + _typeCode_index_3 = [...]uint8{0, 14} + _typeCode_index_4 = [...]uint8{0, 6, 17, 26} + _typeCode_index_5 = [...]uint8{0, 10} + _typeCode_index_6 = [...]uint8{0, 10, 22, 31, 43} + _typeCode_index_7 = [...]uint8{0, 10, 17} + _typeCode_index_8 = [...]uint8{0, 8} +) + +func (i typeCode) String() string { + switch { + case 0 <= i && i <= 16: + return _typeCode_name_0[_typeCode_index_0[i]:_typeCode_index_0[i+1]] + case 25 <= i && i <= 33: + i -= 25 + return _typeCode_name_1[_typeCode_index_1[i]:_typeCode_index_1[i+1]] + case 35 <= i && i <= 38: + i -= 35 + return _typeCode_name_2[_typeCode_index_2[i]:_typeCode_index_2[i+1]] + case i == 47: + return _typeCode_name_3 + case 51 <= i && i <= 53: + i -= 51 + return _typeCode_name_4[_typeCode_index_4[i]:_typeCode_index_4[i+1]] + case i == 55: + return _typeCode_name_5 + case 61 <= i && i <= 64: + i -= 61 + return _typeCode_name_6[_typeCode_index_6[i]:_typeCode_index_6[i+1]] + case 74 <= i && i <= 75: + i -= 74 + return _typeCode_name_7[_typeCode_index_7[i]:_typeCode_index_7[i+1]] + case i == 80: + return _typeCode_name_8 + default: + return fmt.Sprintf("typeCode(%d)", i) + } +} diff --git a/vendor/github.com/SAP/go-hdb/internal/protocol/unsafe.go b/vendor/github.com/SAP/go-hdb/internal/protocol/unsafe.go new file mode 100644 index 000000000000..a8bcaf32413b --- /dev/null +++ b/vendor/github.com/SAP/go-hdb/internal/protocol/unsafe.go @@ -0,0 +1,47 @@ +/* +Copyright 2014 SAP SE + +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 protocol + +import "unsafe" + +func init() { + //check if pointer could be stored in uint64 + var ptr uintptr + var ui64 uint64 + + if unsafe.Sizeof(ptr) > unsafe.Sizeof(ui64) { + panic("pointer size exceeds uint64 size") + } +} + +// LobWriteDescrToPointer returns a pointer to a LobWriteDescr compatible to sql/driver/Value (int64). +func LobWriteDescrToPointer(w *LobWriteDescr) int64 { + return int64(uintptr(unsafe.Pointer(w))) +} + +func pointerToLobWriteDescr(ptr int64) *LobWriteDescr { + return (*LobWriteDescr)(unsafe.Pointer(uintptr(ptr))) +} + +func lobReadDescrToPointer(r *LobReadDescr) int64 { + return int64(uintptr(unsafe.Pointer(r))) +} + +// PointerToLobReadDescr returns the address of a LobReadDescr from an sql/driver/Value (int64) compatible pointer. +func PointerToLobReadDescr(ptr int64) *LobReadDescr { + return (*LobReadDescr)(unsafe.Pointer(uintptr(ptr))) +} diff --git a/vendor/github.com/SAP/go-hdb/internal/unicode/cesu8/cesu8.go b/vendor/github.com/SAP/go-hdb/internal/unicode/cesu8/cesu8.go new file mode 100644 index 000000000000..882dbf62bb8e --- /dev/null +++ b/vendor/github.com/SAP/go-hdb/internal/unicode/cesu8/cesu8.go @@ -0,0 +1,240 @@ +/* +Copyright 2014 SAP SE + +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 cesu8 implements functions and constants to support text encoded in CESU-8. +// It implements functions comparable to the unicode/utf8 package for UTF-8 de- and encoding. +package cesu8 + +import ( + "unicode/utf16" + "unicode/utf8" +) + +const ( + // CESUMax is the maximum amount of bytes used by an CESU-8 codepoint encoding. + CESUMax = 6 +) + +// Size returns the amount of bytes needed to encode an UTF-8 byte slice to CESU-8. +func Size(p []byte) int { + n := 0 + for i := 0; i < len(p); { + r, size, _ := decodeRune(p[i:]) + i += size + n += RuneLen(r) + } + return n +} + +// StringSize is like Size with a string as parameter. +func StringSize(s string) int { + n := 0 + for _, r := range s { + n += RuneLen(r) + } + return n +} + +// EncodeRune writes into p (which must be large enough) the CESU-8 encoding of the rune. It returns the number of bytes written. +func EncodeRune(p []byte, r rune) int { + if r <= rune3Max { + return encodeRune(p, r) + } + high, low := utf16.EncodeRune(r) + n := encodeRune(p, high) + n += encodeRune(p[n:], low) + return n +} + +// FullRune reports whether the bytes in p begin with a full CESU-8 encoding of a rune. +func FullRune(p []byte) bool { + high, n, short := decodeRune(p) + if short { + return false + } + if !utf16.IsSurrogate(high) { + return true + } + _, _, short = decodeRune(p[n:]) + return !short +} + +// DecodeRune unpacks the first CESU-8 encoding in p and returns the rune and its width in bytes. +func DecodeRune(p []byte) (rune, int) { + high, n1, _ := decodeRune(p) + if !utf16.IsSurrogate(high) { + return high, n1 + } + low, n2, _ := decodeRune(p[n1:]) + if low == utf8.RuneError { + return low, n1 + n2 + } + return utf16.DecodeRune(high, low), n1 + n2 +} + +// RuneLen returns the number of bytes required to encode the rune. +func RuneLen(r rune) int { + switch { + case r < 0: + return -1 + case r <= rune1Max: + return 1 + case r <= rune2Max: + return 2 + case r <= rune3Max: + return 3 + case r <= utf8.MaxRune: + return CESUMax + } + return -1 +} + +// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +// Copied from unicode utf8 +// - allow utf8 encoding of utf16 surrogate values +// - see (*) for code changes + +// Code points in the surrogate range are not valid for UTF-8. +const ( + surrogateMin = 0xD800 + surrogateMax = 0xDFFF +) + +const ( + t1 = 0x00 // 0000 0000 + tx = 0x80 // 1000 0000 + t2 = 0xC0 // 1100 0000 + t3 = 0xE0 // 1110 0000 + t4 = 0xF0 // 1111 0000 + t5 = 0xF8 // 1111 1000 + + maskx = 0x3F // 0011 1111 + mask2 = 0x1F // 0001 1111 + mask3 = 0x0F // 0000 1111 + mask4 = 0x07 // 0000 0111 + + rune1Max = 1<<7 - 1 + rune2Max = 1<<11 - 1 + rune3Max = 1<<16 - 1 +) + +func encodeRune(p []byte, r rune) int { + // Negative values are erroneous. Making it unsigned addresses the problem. + switch i := uint32(r); { + case i <= rune1Max: + p[0] = byte(r) + return 1 + case i <= rune2Max: + p[0] = t2 | byte(r>>6) + p[1] = tx | byte(r)&maskx + return 2 + //case i > MaxRune, surrogateMin <= i && i <= surrogateMax: // replaced (*) + case i > utf8.MaxRune: // (*) + r = utf8.RuneError + fallthrough + case i <= rune3Max: + p[0] = t3 | byte(r>>12) + p[1] = tx | byte(r>>6)&maskx + p[2] = tx | byte(r)&maskx + return 3 + default: + p[0] = t4 | byte(r>>18) + p[1] = tx | byte(r>>12)&maskx + p[2] = tx | byte(r>>6)&maskx + p[3] = tx | byte(r)&maskx + return 4 + } +} + +func decodeRune(p []byte) (r rune, size int, short bool) { + n := len(p) + if n < 1 { + return utf8.RuneError, 0, true + } + c0 := p[0] + + // 1-byte, 7-bit sequence? + if c0 < tx { + return rune(c0), 1, false + } + + // unexpected continuation byte? + if c0 < t2 { + return utf8.RuneError, 1, false + } + + // need first continuation byte + if n < 2 { + return utf8.RuneError, 1, true + } + c1 := p[1] + if c1 < tx || t2 <= c1 { + return utf8.RuneError, 1, false + } + + // 2-byte, 11-bit sequence? + if c0 < t3 { + r = rune(c0&mask2)<<6 | rune(c1&maskx) + if r <= rune1Max { + return utf8.RuneError, 1, false + } + return r, 2, false + } + + // need second continuation byte + if n < 3 { + return utf8.RuneError, 1, true + } + c2 := p[2] + if c2 < tx || t2 <= c2 { + return utf8.RuneError, 1, false + } + + // 3-byte, 16-bit sequence? + if c0 < t4 { + r = rune(c0&mask3)<<12 | rune(c1&maskx)<<6 | rune(c2&maskx) + if r <= rune2Max { + return utf8.RuneError, 1, false + } + // do not throw error on surrogates // (*) + //if surrogateMin <= r && r <= surrogateMax { + // return RuneError, 1, false + //} + return r, 3, false + } + + // need third continuation byte + if n < 4 { + return utf8.RuneError, 1, true + } + c3 := p[3] + if c3 < tx || t2 <= c3 { + return utf8.RuneError, 1, false + } + + // 4-byte, 21-bit sequence? + if c0 < t5 { + r = rune(c0&mask4)<<18 | rune(c1&maskx)<<12 | rune(c2&maskx)<<6 | rune(c3&maskx) + if r <= rune3Max || utf8.MaxRune < r { + return utf8.RuneError, 1, false + } + return r, 4, false + } + + // error + return utf8.RuneError, 1, false +} diff --git a/vendor/github.com/SAP/go-hdb/internal/unicode/unicode.go b/vendor/github.com/SAP/go-hdb/internal/unicode/unicode.go new file mode 100644 index 000000000000..be9cf3cb9750 --- /dev/null +++ b/vendor/github.com/SAP/go-hdb/internal/unicode/unicode.go @@ -0,0 +1,111 @@ +/* +Copyright 2014 SAP SE + +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 unicode implements UTF-8 to CESU-8 and vice versa transformations. +package unicode + +import ( + "errors" + "unicode/utf8" + + "github.com/SAP/go-hdb/internal/unicode/cesu8" + "golang.org/x/text/transform" +) + +var ( + // Utf8ToCesu8Transformer implements the golang.org/x/text/transform/Transformer interface for UTF-8 to CESU-8 transformation. + Utf8ToCesu8Transformer = new(utf8ToCesu8Transformer) + // Cesu8ToUtf8Transformer implements the golang.org/x/text/transform/Transformer interface for CESU-8 to UTF-8 transformation. + Cesu8ToUtf8Transformer = new(cesu8ToUtf8Transformer) + // ErrInvalidUtf8 means that a transformer detected invalid UTF-8 data. + ErrInvalidUtf8 = errors.New("Invalid UTF-8") + // ErrInvalidCesu8 means that a transformer detected invalid CESU-8 data. + ErrInvalidCesu8 = errors.New("Invalid CESU-8") +) + +type utf8ToCesu8Transformer struct{ transform.NopResetter } + +func (t *utf8ToCesu8Transformer) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) { + i, j := 0, 0 + for i < len(src) { + if src[i] < utf8.RuneSelf { + if j < len(dst) { + dst[j] = src[i] + i++ + j++ + } else { + return j, i, transform.ErrShortDst + } + } else { + if !utf8.FullRune(src[i:]) { + return j, i, transform.ErrShortSrc + } + r, n := utf8.DecodeRune(src[i:]) + if r == utf8.RuneError { + return j, i, ErrInvalidUtf8 + } + m := cesu8.RuneLen(r) + if m == -1 { + panic("internal UTF-8 to CESU-8 transformation error") + } + if j+m <= len(dst) { + cesu8.EncodeRune(dst[j:], r) + i += n + j += m + } else { + return j, i, transform.ErrShortDst + } + } + } + return j, i, nil +} + +type cesu8ToUtf8Transformer struct{ transform.NopResetter } + +func (t *cesu8ToUtf8Transformer) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) { + i, j := 0, 0 + for i < len(src) { + if src[i] < utf8.RuneSelf { + if j < len(dst) { + dst[j] = src[i] + i++ + j++ + } else { + return j, i, transform.ErrShortDst + } + } else { + if !cesu8.FullRune(src[i:]) { + return j, i, transform.ErrShortSrc + } + r, n := cesu8.DecodeRune(src[i:]) + if r == utf8.RuneError { + return j, i, ErrInvalidCesu8 + } + m := utf8.RuneLen(r) + if m == -1 { + panic("internal CESU-8 to UTF-8 transformation error") + } + if j+m <= len(dst) { + utf8.EncodeRune(dst[j:], r) + i += n + j += m + } else { + return j, i, transform.ErrShortDst + } + } + } + return j, i, nil +} diff --git a/vendor/golang.org/x/text/AUTHORS b/vendor/golang.org/x/text/AUTHORS new file mode 100644 index 000000000000..15167cd746c5 --- /dev/null +++ b/vendor/golang.org/x/text/AUTHORS @@ -0,0 +1,3 @@ +# This source code refers to The Go Authors for copyright purposes. +# The master list of authors is in the main Go distribution, +# visible at http://tip.golang.org/AUTHORS. diff --git a/vendor/golang.org/x/text/CONTRIBUTING.md b/vendor/golang.org/x/text/CONTRIBUTING.md new file mode 100644 index 000000000000..88dff59bc7d2 --- /dev/null +++ b/vendor/golang.org/x/text/CONTRIBUTING.md @@ -0,0 +1,31 @@ +# Contributing to Go + +Go is an open source project. + +It is the work of hundreds of contributors. We appreciate your help! + + +## Filing issues + +When [filing an issue](https://golang.org/issue/new), make sure to answer these five questions: + +1. What version of Go are you using (`go version`)? +2. What operating system and processor architecture are you using? +3. What did you do? +4. What did you expect to see? +5. What did you see instead? + +General questions should go to the [golang-nuts mailing list](https://groups.google.com/group/golang-nuts) instead of the issue tracker. +The gophers there will answer or ask you to file an issue if you've tripped over a bug. + +## Contributing code + +Please read the [Contribution Guidelines](https://golang.org/doc/contribute.html) +before sending patches. + +**We do not accept GitHub pull requests** +(we use [Gerrit](https://code.google.com/p/gerrit/) instead for code review). + +Unless otherwise noted, the Go source files are distributed under +the BSD-style license found in the LICENSE file. + diff --git a/vendor/golang.org/x/text/CONTRIBUTORS b/vendor/golang.org/x/text/CONTRIBUTORS new file mode 100644 index 000000000000..1c4577e96806 --- /dev/null +++ b/vendor/golang.org/x/text/CONTRIBUTORS @@ -0,0 +1,3 @@ +# This source code was written by the Go contributors. +# The master list of contributors is in the main Go distribution, +# visible at http://tip.golang.org/CONTRIBUTORS. diff --git a/vendor/golang.org/x/text/README b/vendor/golang.org/x/text/README new file mode 100644 index 000000000000..4826fe8fb6a9 --- /dev/null +++ b/vendor/golang.org/x/text/README @@ -0,0 +1,23 @@ +This repository holds supplementary Go libraries for text processing, many involving Unicode. + +To submit changes to this repository, see http://golang.org/doc/contribute.html. + +To generate the tables in this repository (except for the encoding tables), +run go generate from this directory. By default tables are generated for the +Unicode version in core and the CLDR version defined in +golang.org/x/text/unicode/cldr. + +Running go generate will as a side effect create a DATA subdirectory in this +directory which holds all files that are used as a source for generating the +tables. This directory will also serve as a cache. + +Run + + go test ./... + +from this directory to run all tests. Add the "-tags icu" flag to also run +ICU conformance tests (if available). This requires that you have the correct +ICU version installed on your system. + +TODO: +- updating unversioned source files. \ No newline at end of file diff --git a/vendor/golang.org/x/text/codereview.cfg b/vendor/golang.org/x/text/codereview.cfg new file mode 100644 index 000000000000..3f8b14b64e83 --- /dev/null +++ b/vendor/golang.org/x/text/codereview.cfg @@ -0,0 +1 @@ +issuerepo: golang/go diff --git a/vendor/golang.org/x/text/doc.go b/vendor/golang.org/x/text/doc.go new file mode 100644 index 000000000000..a48e2843fcd0 --- /dev/null +++ b/vendor/golang.org/x/text/doc.go @@ -0,0 +1,13 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:generate go run gen.go + +// text is a repository of text-related packages related to internationalization +// (i18n) and localization (l10n), such as character encodings, text +// transformations, and locale-specific text handling. +package text + +// TODO: more documentation on general concepts, such as Transformers, use +// of normalization, etc. diff --git a/vendor/golang.org/x/text/gen.go b/vendor/golang.org/x/text/gen.go new file mode 100644 index 000000000000..79af97e708b1 --- /dev/null +++ b/vendor/golang.org/x/text/gen.go @@ -0,0 +1,292 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build ignore + +// gen runs go generate on Unicode- and CLDR-related package in the text +// repositories, taking into account dependencies and versions. +package main + +import ( + "bytes" + "flag" + "fmt" + "go/build" + "go/format" + "io/ioutil" + "os" + "os/exec" + "path" + "path/filepath" + "regexp" + "runtime" + "strings" + "sync" + "unicode" + + "golang.org/x/text/internal/gen" +) + +var ( + verbose = flag.Bool("v", false, "verbose output") + force = flag.Bool("force", false, "ignore failing dependencies") + doCore = flag.Bool("core", false, "force an update to core") + excludeList = flag.String("exclude", "", + "comma-separated list of packages to exclude") + + // The user can specify a selection of packages to build on the command line. + args []string +) + +func exclude(pkg string) bool { + if len(args) > 0 { + return !contains(args, pkg) + } + return contains(strings.Split(*excludeList, ","), pkg) +} + +// TODO: +// - Better version handling. +// - Generate tables for the core unicode package? +// - Add generation for encodings. This requires some retooling here and there. +// - Running repo-wide "long" tests. + +var vprintf = fmt.Printf + +func main() { + gen.Init() + args = flag.Args() + if !*verbose { + // Set vprintf to a no-op. + vprintf = func(string, ...interface{}) (int, error) { return 0, nil } + } + + // TODO: create temporary cache directory to load files and create and set + // a "cache" option if the user did not specify the UNICODE_DIR environment + // variable. This will prevent duplicate downloads and also will enable long + // tests, which really need to be run after each generated package. + + updateCore := *doCore + if gen.UnicodeVersion() != unicode.Version { + fmt.Printf("Requested Unicode version %s; core unicode version is %s.\n", + gen.UnicodeVersion(), + unicode.Version) + // TODO: use collate to compare. Simple comparison will work, though, + // until Unicode reaches version 10. To avoid circular dependencies, we + // could use the NumericWeighter without using package collate using a + // trivial Weighter implementation. + if gen.UnicodeVersion() < unicode.Version && !*force { + os.Exit(2) + } + updateCore = true + } + + var unicode = &dependency{} + if updateCore { + fmt.Printf("Updating core to version %s...\n", gen.UnicodeVersion()) + unicode = generate("unicode") + + // Test some users of the unicode packages, especially the ones that + // keep a mirrored table. These may need to be corrected by hand. + generate("regexp", unicode) + generate("strconv", unicode) // mimics Unicode table + generate("strings", unicode) + generate("testing", unicode) // mimics Unicode table + } + + var ( + cldr = generate("./unicode/cldr", unicode) + language = generate("./language", cldr) + internal = generate("./internal", unicode, language) + norm = generate("./unicode/norm", unicode) + rangetable = generate("./unicode/rangetable", unicode) + cases = generate("./cases", unicode, norm, language, rangetable) + width = generate("./width", unicode) + bidi = generate("./unicode/bidi", unicode, norm, rangetable) + mib = generate("./encoding/internal/identifier", unicode) + _ = generate("./encoding/htmlindex", unicode, language, mib) + _ = generate("./encoding/ianaindex", unicode, language, mib) + _ = generate("./secure/precis", unicode, norm, rangetable, cases, width, bidi) + _ = generate("./currency", unicode, cldr, language, internal) + _ = generate("./internal/number", unicode, cldr, language, internal) + _ = generate("./feature/plural", unicode, cldr, language, internal) + _ = generate("./internal/export/idna", unicode, bidi, norm) + _ = generate("./language/display", unicode, cldr, language, internal) + _ = generate("./collate", unicode, norm, cldr, language, rangetable) + _ = generate("./search", unicode, norm, cldr, language, rangetable) + ) + all.Wait() + + // Copy exported packages to the destination golang.org repo. + copyExported("golang.org/x/net/idna") + + if updateCore { + copyVendored() + } + + if hasErrors { + fmt.Println("FAIL") + os.Exit(1) + } + vprintf("SUCCESS\n") +} + +var ( + all sync.WaitGroup + hasErrors bool +) + +type dependency struct { + sync.WaitGroup + hasErrors bool +} + +func generate(pkg string, deps ...*dependency) *dependency { + var wg dependency + if exclude(pkg) { + return &wg + } + wg.Add(1) + all.Add(1) + go func() { + defer wg.Done() + defer all.Done() + // Wait for dependencies to finish. + for _, d := range deps { + d.Wait() + if d.hasErrors && !*force { + fmt.Printf("--- ABORT: %s\n", pkg) + wg.hasErrors = true + return + } + } + vprintf("=== GENERATE %s\n", pkg) + args := []string{"generate"} + if *verbose { + args = append(args, "-v") + } + args = append(args, pkg) + cmd := exec.Command(filepath.Join(runtime.GOROOT(), "bin", "go"), args...) + w := &bytes.Buffer{} + cmd.Stderr = w + cmd.Stdout = w + if err := cmd.Run(); err != nil { + fmt.Printf("--- FAIL: %s:\n\t%v\n\tError: %v\n", pkg, indent(w), err) + hasErrors = true + wg.hasErrors = true + return + } + + vprintf("=== TEST %s\n", pkg) + args[0] = "test" + cmd = exec.Command(filepath.Join(runtime.GOROOT(), "bin", "go"), args...) + wt := &bytes.Buffer{} + cmd.Stderr = wt + cmd.Stdout = wt + if err := cmd.Run(); err != nil { + fmt.Printf("--- FAIL: %s:\n\t%v\n\tError: %v\n", pkg, indent(wt), err) + hasErrors = true + wg.hasErrors = true + return + } + vprintf("--- SUCCESS: %s\n\t%v\n", pkg, indent(w)) + fmt.Print(wt.String()) + }() + return &wg +} + +// copyExported copies a package in x/text/internal/export to the +// destination repository. +func copyExported(p string) { + copyPackage( + filepath.Join("internal", "export", path.Base(p)), + filepath.Join("..", filepath.FromSlash(p[len("golang.org/x"):])), + "golang.org/x/text/internal/export/"+path.Base(p), + p) +} + +// copyVendored copies packages used by Go core into the vendored directory. +func copyVendored() { + root := filepath.Join(build.Default.GOROOT, filepath.FromSlash("src/vendor/golang_org/x")) + + err := filepath.Walk(root, func(dir string, info os.FileInfo, err error) error { + if err != nil || !info.IsDir() || root == dir { + return err + } + src := dir[len(root)+1:] + const slash = string(filepath.Separator) + if c := strings.Split(src, slash); c[0] == "text" { + // Copy a text repo package from its normal location. + src = strings.Join(c[1:], slash) + } else { + // Copy the vendored package if it exists in the export directory. + src = filepath.Join("internal", "export", filepath.Base(src)) + } + copyPackage(src, dir, "golang.org", "golang_org") + return nil + }) + if err != nil { + fmt.Printf("Seeding directory %s has failed %v:", root, err) + os.Exit(1) + } +} + +// goGenRE is used to remove go:generate lines. +var goGenRE = regexp.MustCompile("//go:generate[^\n]*\n") + +// copyPackage copies relevant files from a directory in x/text to the +// destination package directory. The destination package is assumed to have +// the same name. For each copied file go:generate lines are removed and +// and package comments are rewritten to the new path. +func copyPackage(dirSrc, dirDst, search, replace string) { + err := filepath.Walk(dirSrc, func(file string, info os.FileInfo, err error) error { + base := filepath.Base(file) + if err != nil || info.IsDir() || + !strings.HasSuffix(base, ".go") || + strings.HasSuffix(base, "_test.go") && !strings.HasPrefix(base, "example") || + // Don't process subdirectories. + filepath.Dir(file) != dirSrc { + return nil + } + b, err := ioutil.ReadFile(file) + if err != nil || bytes.Contains(b, []byte("\n// +build ignore")) { + return err + } + // Fix paths. + b = bytes.Replace(b, []byte(search), []byte(replace), -1) + // Remove go:generate lines. + b = goGenRE.ReplaceAllLiteral(b, nil) + comment := "// Code generated by running \"go generate\" in golang.org/x/text. DO NOT EDIT.\n\n" + if *doCore { + comment = "// Code generated by running \"go run gen.go -core\" in golang.org/x/text. DO NOT EDIT.\n\n" + } + if !bytes.HasPrefix(b, []byte(comment)) { + b = append([]byte(comment), b...) + } + if b, err = format.Source(b); err != nil { + fmt.Println("Failed to format file:", err) + os.Exit(1) + } + file = filepath.Join(dirDst, base) + vprintf("=== COPY %s\n", file) + return ioutil.WriteFile(file, b, 0666) + }) + if err != nil { + fmt.Println("Copying exported files failed:", err) + os.Exit(1) + } +} + +func contains(a []string, s string) bool { + for _, e := range a { + if s == e { + return true + } + } + return false +} + +func indent(b *bytes.Buffer) string { + return strings.Replace(strings.TrimSpace(b.String()), "\n", "\n\t", -1) +} diff --git a/vendor/golang.org/x/text/internal/gen/code.go b/vendor/golang.org/x/text/internal/gen/code.go new file mode 100644 index 000000000000..d7031b6945a2 --- /dev/null +++ b/vendor/golang.org/x/text/internal/gen/code.go @@ -0,0 +1,351 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package gen + +import ( + "bytes" + "encoding/gob" + "fmt" + "hash" + "hash/fnv" + "io" + "log" + "os" + "reflect" + "strings" + "unicode" + "unicode/utf8" +) + +// This file contains utilities for generating code. + +// TODO: other write methods like: +// - slices, maps, types, etc. + +// CodeWriter is a utility for writing structured code. It computes the content +// hash and size of written content. It ensures there are newlines between +// written code blocks. +type CodeWriter struct { + buf bytes.Buffer + Size int + Hash hash.Hash32 // content hash + gob *gob.Encoder + // For comments we skip the usual one-line separator if they are followed by + // a code block. + skipSep bool +} + +func (w *CodeWriter) Write(p []byte) (n int, err error) { + return w.buf.Write(p) +} + +// NewCodeWriter returns a new CodeWriter. +func NewCodeWriter() *CodeWriter { + h := fnv.New32() + return &CodeWriter{Hash: h, gob: gob.NewEncoder(h)} +} + +// WriteGoFile appends the buffer with the total size of all created structures +// and writes it as a Go file to the the given file with the given package name. +func (w *CodeWriter) WriteGoFile(filename, pkg string) { + f, err := os.Create(filename) + if err != nil { + log.Fatalf("Could not create file %s: %v", filename, err) + } + defer f.Close() + if _, err = w.WriteGo(f, pkg); err != nil { + log.Fatalf("Error writing file %s: %v", filename, err) + } +} + +// WriteGo appends the buffer with the total size of all created structures and +// writes it as a Go file to the the given writer with the given package name. +func (w *CodeWriter) WriteGo(out io.Writer, pkg string) (n int, err error) { + sz := w.Size + w.WriteComment("Total table size %d bytes (%dKiB); checksum: %X\n", sz, sz/1024, w.Hash.Sum32()) + defer w.buf.Reset() + return WriteGo(out, pkg, w.buf.Bytes()) +} + +func (w *CodeWriter) printf(f string, x ...interface{}) { + fmt.Fprintf(w, f, x...) +} + +func (w *CodeWriter) insertSep() { + if w.skipSep { + w.skipSep = false + return + } + // Use at least two newlines to ensure a blank space between the previous + // block. WriteGoFile will remove extraneous newlines. + w.printf("\n\n") +} + +// WriteComment writes a comment block. All line starts are prefixed with "//". +// Initial empty lines are gobbled. The indentation for the first line is +// stripped from consecutive lines. +func (w *CodeWriter) WriteComment(comment string, args ...interface{}) { + s := fmt.Sprintf(comment, args...) + s = strings.Trim(s, "\n") + + // Use at least two newlines to ensure a blank space between the previous + // block. WriteGoFile will remove extraneous newlines. + w.printf("\n\n// ") + w.skipSep = true + + // strip first indent level. + sep := "\n" + for ; len(s) > 0 && (s[0] == '\t' || s[0] == ' '); s = s[1:] { + sep += s[:1] + } + + strings.NewReplacer(sep, "\n// ", "\n", "\n// ").WriteString(w, s) + + w.printf("\n") +} + +func (w *CodeWriter) writeSizeInfo(size int) { + w.printf("// Size: %d bytes\n", size) +} + +// WriteConst writes a constant of the given name and value. +func (w *CodeWriter) WriteConst(name string, x interface{}) { + w.insertSep() + v := reflect.ValueOf(x) + + switch v.Type().Kind() { + case reflect.String: + w.printf("const %s %s = ", name, typeName(x)) + w.WriteString(v.String()) + w.printf("\n") + default: + w.printf("const %s = %#v\n", name, x) + } +} + +// WriteVar writes a variable of the given name and value. +func (w *CodeWriter) WriteVar(name string, x interface{}) { + w.insertSep() + v := reflect.ValueOf(x) + oldSize := w.Size + sz := int(v.Type().Size()) + w.Size += sz + + switch v.Type().Kind() { + case reflect.String: + w.printf("var %s %s = ", name, typeName(x)) + w.WriteString(v.String()) + case reflect.Struct: + w.gob.Encode(x) + fallthrough + case reflect.Slice, reflect.Array: + w.printf("var %s = ", name) + w.writeValue(v) + w.writeSizeInfo(w.Size - oldSize) + default: + w.printf("var %s %s = ", name, typeName(x)) + w.gob.Encode(x) + w.writeValue(v) + w.writeSizeInfo(w.Size - oldSize) + } + w.printf("\n") +} + +func (w *CodeWriter) writeValue(v reflect.Value) { + x := v.Interface() + switch v.Kind() { + case reflect.String: + w.WriteString(v.String()) + case reflect.Array: + // Don't double count: callers of WriteArray count on the size being + // added, so we need to discount it here. + w.Size -= int(v.Type().Size()) + w.writeSlice(x, true) + case reflect.Slice: + w.writeSlice(x, false) + case reflect.Struct: + w.printf("%s{\n", typeName(v.Interface())) + t := v.Type() + for i := 0; i < v.NumField(); i++ { + w.printf("%s: ", t.Field(i).Name) + w.writeValue(v.Field(i)) + w.printf(",\n") + } + w.printf("}") + default: + w.printf("%#v", x) + } +} + +// WriteString writes a string literal. +func (w *CodeWriter) WriteString(s string) { + s = strings.Replace(s, `\`, `\\`, -1) + io.WriteString(w.Hash, s) // content hash + w.Size += len(s) + + const maxInline = 40 + if len(s) <= maxInline { + w.printf("%q", s) + return + } + + // We will render the string as a multi-line string. + const maxWidth = 80 - 4 - len(`"`) - len(`" +`) + + // When starting on its own line, go fmt indents line 2+ an extra level. + n, max := maxWidth, maxWidth-4 + + // As per https://golang.org/issue/18078, the compiler has trouble + // compiling the concatenation of many strings, s0 + s1 + s2 + ... + sN, + // for large N. We insert redundant, explicit parentheses to work around + // that, lowering the N at any given step: (s0 + s1 + ... + s63) + (s64 + + // ... + s127) + etc + (etc + ... + sN). + explicitParens, extraComment := len(s) > 128*1024, "" + if explicitParens { + w.printf(`(`) + extraComment = "; the redundant, explicit parens are for https://golang.org/issue/18078" + } + + // Print "" +\n, if a string does not start on its own line. + b := w.buf.Bytes() + if p := len(bytes.TrimRight(b, " \t")); p > 0 && b[p-1] != '\n' { + w.printf("\"\" + // Size: %d bytes%s\n", len(s), extraComment) + n, max = maxWidth, maxWidth + } + + w.printf(`"`) + + for sz, p, nLines := 0, 0, 0; p < len(s); { + var r rune + r, sz = utf8.DecodeRuneInString(s[p:]) + out := s[p : p+sz] + chars := 1 + if !unicode.IsPrint(r) || r == utf8.RuneError || r == '"' { + switch sz { + case 1: + out = fmt.Sprintf("\\x%02x", s[p]) + case 2, 3: + out = fmt.Sprintf("\\u%04x", r) + case 4: + out = fmt.Sprintf("\\U%08x", r) + } + chars = len(out) + } + if n -= chars; n < 0 { + nLines++ + if explicitParens && nLines&63 == 63 { + w.printf("\") + (\"") + } + w.printf("\" +\n\"") + n = max - len(out) + } + w.printf("%s", out) + p += sz + } + w.printf(`"`) + if explicitParens { + w.printf(`)`) + } +} + +// WriteSlice writes a slice value. +func (w *CodeWriter) WriteSlice(x interface{}) { + w.writeSlice(x, false) +} + +// WriteArray writes an array value. +func (w *CodeWriter) WriteArray(x interface{}) { + w.writeSlice(x, true) +} + +func (w *CodeWriter) writeSlice(x interface{}, isArray bool) { + v := reflect.ValueOf(x) + w.gob.Encode(v.Len()) + w.Size += v.Len() * int(v.Type().Elem().Size()) + name := typeName(x) + if isArray { + name = fmt.Sprintf("[%d]%s", v.Len(), name[strings.Index(name, "]")+1:]) + } + if isArray { + w.printf("%s{\n", name) + } else { + w.printf("%s{ // %d elements\n", name, v.Len()) + } + + switch kind := v.Type().Elem().Kind(); kind { + case reflect.String: + for _, s := range x.([]string) { + w.WriteString(s) + w.printf(",\n") + } + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, + reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + // nLine and nBlock are the number of elements per line and block. + nLine, nBlock, format := 8, 64, "%d," + switch kind { + case reflect.Uint8: + format = "%#02x," + case reflect.Uint16: + format = "%#04x," + case reflect.Uint32: + nLine, nBlock, format = 4, 32, "%#08x," + case reflect.Uint, reflect.Uint64: + nLine, nBlock, format = 4, 32, "%#016x," + case reflect.Int8: + nLine = 16 + } + n := nLine + for i := 0; i < v.Len(); i++ { + if i%nBlock == 0 && v.Len() > nBlock { + w.printf("// Entry %X - %X\n", i, i+nBlock-1) + } + x := v.Index(i).Interface() + w.gob.Encode(x) + w.printf(format, x) + if n--; n == 0 { + n = nLine + w.printf("\n") + } + } + w.printf("\n") + case reflect.Struct: + zero := reflect.Zero(v.Type().Elem()).Interface() + for i := 0; i < v.Len(); i++ { + x := v.Index(i).Interface() + w.gob.EncodeValue(v) + if !reflect.DeepEqual(zero, x) { + line := fmt.Sprintf("%#v,\n", x) + line = line[strings.IndexByte(line, '{'):] + w.printf("%d: ", i) + w.printf(line) + } + } + case reflect.Array: + for i := 0; i < v.Len(); i++ { + w.printf("%d: %#v,\n", i, v.Index(i).Interface()) + } + default: + panic("gen: slice elem type not supported") + } + w.printf("}") +} + +// WriteType writes a definition of the type of the given value and returns the +// type name. +func (w *CodeWriter) WriteType(x interface{}) string { + t := reflect.TypeOf(x) + w.printf("type %s struct {\n", t.Name()) + for i := 0; i < t.NumField(); i++ { + w.printf("\t%s %s\n", t.Field(i).Name, t.Field(i).Type) + } + w.printf("}\n") + return t.Name() +} + +// typeName returns the name of the go type of x. +func typeName(x interface{}) string { + t := reflect.ValueOf(x).Type() + return strings.Replace(fmt.Sprint(t), "main.", "", 1) +} diff --git a/vendor/golang.org/x/text/internal/gen/gen.go b/vendor/golang.org/x/text/internal/gen/gen.go new file mode 100644 index 000000000000..2acb0355a26a --- /dev/null +++ b/vendor/golang.org/x/text/internal/gen/gen.go @@ -0,0 +1,281 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package gen contains common code for the various code generation tools in the +// text repository. Its usage ensures consistency between tools. +// +// This package defines command line flags that are common to most generation +// tools. The flags allow for specifying specific Unicode and CLDR versions +// in the public Unicode data repository (http://www.unicode.org/Public). +// +// A local Unicode data mirror can be set through the flag -local or the +// environment variable UNICODE_DIR. The former takes precedence. The local +// directory should follow the same structure as the public repository. +// +// IANA data can also optionally be mirrored by putting it in the iana directory +// rooted at the top of the local mirror. Beware, though, that IANA data is not +// versioned. So it is up to the developer to use the right version. +package gen // import "golang.org/x/text/internal/gen" + +import ( + "bytes" + "flag" + "fmt" + "go/build" + "go/format" + "io" + "io/ioutil" + "log" + "net/http" + "os" + "path" + "path/filepath" + "sync" + "unicode" + + "golang.org/x/text/unicode/cldr" +) + +var ( + url = flag.String("url", + "http://www.unicode.org/Public", + "URL of Unicode database directory") + iana = flag.String("iana", + "http://www.iana.org", + "URL of the IANA repository") + unicodeVersion = flag.String("unicode", + getEnv("UNICODE_VERSION", unicode.Version), + "unicode version to use") + cldrVersion = flag.String("cldr", + getEnv("CLDR_VERSION", cldr.Version), + "cldr version to use") +) + +func getEnv(name, def string) string { + if v := os.Getenv(name); v != "" { + return v + } + return def +} + +// Init performs common initialization for a gen command. It parses the flags +// and sets up the standard logging parameters. +func Init() { + log.SetPrefix("") + log.SetFlags(log.Lshortfile) + flag.Parse() +} + +const header = `// Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. + +package %s + +` + +// UnicodeVersion reports the requested Unicode version. +func UnicodeVersion() string { + return *unicodeVersion +} + +// UnicodeVersion reports the requested CLDR version. +func CLDRVersion() string { + return *cldrVersion +} + +// IsLocal reports whether data files are available locally. +func IsLocal() bool { + dir, err := localReadmeFile() + if err != nil { + return false + } + if _, err = os.Stat(dir); err != nil { + return false + } + return true +} + +// OpenUCDFile opens the requested UCD file. The file is specified relative to +// the public Unicode root directory. It will call log.Fatal if there are any +// errors. +func OpenUCDFile(file string) io.ReadCloser { + return openUnicode(path.Join(*unicodeVersion, "ucd", file)) +} + +// OpenCLDRCoreZip opens the CLDR core zip file. It will call log.Fatal if there +// are any errors. +func OpenCLDRCoreZip() io.ReadCloser { + return OpenUnicodeFile("cldr", *cldrVersion, "core.zip") +} + +// OpenUnicodeFile opens the requested file of the requested category from the +// root of the Unicode data archive. The file is specified relative to the +// public Unicode root directory. If version is "", it will use the default +// Unicode version. It will call log.Fatal if there are any errors. +func OpenUnicodeFile(category, version, file string) io.ReadCloser { + if version == "" { + version = UnicodeVersion() + } + return openUnicode(path.Join(category, version, file)) +} + +// OpenIANAFile opens the requested IANA file. The file is specified relative +// to the IANA root, which is typically either http://www.iana.org or the +// iana directory in the local mirror. It will call log.Fatal if there are any +// errors. +func OpenIANAFile(path string) io.ReadCloser { + return Open(*iana, "iana", path) +} + +var ( + dirMutex sync.Mutex + localDir string +) + +const permissions = 0755 + +func localReadmeFile() (string, error) { + p, err := build.Import("golang.org/x/text", "", build.FindOnly) + if err != nil { + return "", fmt.Errorf("Could not locate package: %v", err) + } + return filepath.Join(p.Dir, "DATA", "README"), nil +} + +func getLocalDir() string { + dirMutex.Lock() + defer dirMutex.Unlock() + + readme, err := localReadmeFile() + if err != nil { + log.Fatal(err) + } + dir := filepath.Dir(readme) + if _, err := os.Stat(readme); err != nil { + if err := os.MkdirAll(dir, permissions); err != nil { + log.Fatalf("Could not create directory: %v", err) + } + ioutil.WriteFile(readme, []byte(readmeTxt), permissions) + } + return dir +} + +const readmeTxt = `Generated by golang.org/x/text/internal/gen. DO NOT EDIT. + +This directory contains downloaded files used to generate the various tables +in the golang.org/x/text subrepo. + +Note that the language subtag repo (iana/assignments/language-subtag-registry) +and all other times in the iana subdirectory are not versioned and will need +to be periodically manually updated. The easiest way to do this is to remove +the entire iana directory. This is mostly of concern when updating the language +package. +` + +// Open opens subdir/path if a local directory is specified and the file exists, +// where subdir is a directory relative to the local root, or fetches it from +// urlRoot/path otherwise. It will call log.Fatal if there are any errors. +func Open(urlRoot, subdir, path string) io.ReadCloser { + file := filepath.Join(getLocalDir(), subdir, filepath.FromSlash(path)) + return open(file, urlRoot, path) +} + +func openUnicode(path string) io.ReadCloser { + file := filepath.Join(getLocalDir(), filepath.FromSlash(path)) + return open(file, *url, path) +} + +// TODO: automatically periodically update non-versioned files. + +func open(file, urlRoot, path string) io.ReadCloser { + if f, err := os.Open(file); err == nil { + return f + } + r := get(urlRoot, path) + defer r.Close() + b, err := ioutil.ReadAll(r) + if err != nil { + log.Fatalf("Could not download file: %v", err) + } + os.MkdirAll(filepath.Dir(file), permissions) + if err := ioutil.WriteFile(file, b, permissions); err != nil { + log.Fatalf("Could not create file: %v", err) + } + return ioutil.NopCloser(bytes.NewReader(b)) +} + +func get(root, path string) io.ReadCloser { + url := root + "/" + path + fmt.Printf("Fetching %s...", url) + defer fmt.Println(" done.") + resp, err := http.Get(url) + if err != nil { + log.Fatalf("HTTP GET: %v", err) + } + if resp.StatusCode != 200 { + log.Fatalf("Bad GET status for %q: %q", url, resp.Status) + } + return resp.Body +} + +// TODO: use Write*Version in all applicable packages. + +// WriteUnicodeVersion writes a constant for the Unicode version from which the +// tables are generated. +func WriteUnicodeVersion(w io.Writer) { + fmt.Fprintf(w, "// UnicodeVersion is the Unicode version from which the tables in this package are derived.\n") + fmt.Fprintf(w, "const UnicodeVersion = %q\n\n", UnicodeVersion()) +} + +// WriteCLDRVersion writes a constant for the CLDR version from which the +// tables are generated. +func WriteCLDRVersion(w io.Writer) { + fmt.Fprintf(w, "// CLDRVersion is the CLDR version from which the tables in this package are derived.\n") + fmt.Fprintf(w, "const CLDRVersion = %q\n\n", CLDRVersion()) +} + +// WriteGoFile prepends a standard file comment and package statement to the +// given bytes, applies gofmt, and writes them to a file with the given name. +// It will call log.Fatal if there are any errors. +func WriteGoFile(filename, pkg string, b []byte) { + w, err := os.Create(filename) + if err != nil { + log.Fatalf("Could not create file %s: %v", filename, err) + } + defer w.Close() + if _, err = WriteGo(w, pkg, b); err != nil { + log.Fatalf("Error writing file %s: %v", filename, err) + } +} + +// WriteGo prepends a standard file comment and package statement to the given +// bytes, applies gofmt, and writes them to w. +func WriteGo(w io.Writer, pkg string, b []byte) (n int, err error) { + src := []byte(fmt.Sprintf(header, pkg)) + src = append(src, b...) + formatted, err := format.Source(src) + if err != nil { + // Print the generated code even in case of an error so that the + // returned error can be meaningfully interpreted. + n, _ = w.Write(src) + return n, err + } + return w.Write(formatted) +} + +// Repackage rewrites a Go file from belonging to package main to belonging to +// the given package. +func Repackage(inFile, outFile, pkg string) { + src, err := ioutil.ReadFile(inFile) + if err != nil { + log.Fatalf("reading %s: %v", inFile, err) + } + const toDelete = "package main\n\n" + i := bytes.Index(src, []byte(toDelete)) + if i < 0 { + log.Fatalf("Could not find %q in %s.", toDelete, inFile) + } + w := &bytes.Buffer{} + w.Write(src[i+len(toDelete):]) + WriteGoFile(outFile, pkg, w.Bytes()) +} diff --git a/vendor/golang.org/x/text/unicode/cldr/base.go b/vendor/golang.org/x/text/unicode/cldr/base.go new file mode 100644 index 000000000000..2382f4d6da1a --- /dev/null +++ b/vendor/golang.org/x/text/unicode/cldr/base.go @@ -0,0 +1,100 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cldr + +import ( + "encoding/xml" + "regexp" + "strconv" +) + +// Elem is implemented by every XML element. +type Elem interface { + setEnclosing(Elem) + setName(string) + enclosing() Elem + + GetCommon() *Common +} + +type hidden struct { + CharData string `xml:",chardata"` + Alias *struct { + Common + Source string `xml:"source,attr"` + Path string `xml:"path,attr"` + } `xml:"alias"` + Def *struct { + Common + Choice string `xml:"choice,attr,omitempty"` + Type string `xml:"type,attr,omitempty"` + } `xml:"default"` +} + +// Common holds several of the most common attributes and sub elements +// of an XML element. +type Common struct { + XMLName xml.Name + name string + enclElem Elem + Type string `xml:"type,attr,omitempty"` + Reference string `xml:"reference,attr,omitempty"` + Alt string `xml:"alt,attr,omitempty"` + ValidSubLocales string `xml:"validSubLocales,attr,omitempty"` + Draft string `xml:"draft,attr,omitempty"` + hidden +} + +// Default returns the default type to select from the enclosed list +// or "" if no default value is specified. +func (e *Common) Default() string { + if e.Def == nil { + return "" + } + if e.Def.Choice != "" { + return e.Def.Choice + } else if e.Def.Type != "" { + // Type is still used by the default element in collation. + return e.Def.Type + } + return "" +} + +// GetCommon returns e. It is provided such that Common implements Elem. +func (e *Common) GetCommon() *Common { + return e +} + +// Data returns the character data accumulated for this element. +func (e *Common) Data() string { + e.CharData = charRe.ReplaceAllStringFunc(e.CharData, replaceUnicode) + return e.CharData +} + +func (e *Common) setName(s string) { + e.name = s +} + +func (e *Common) enclosing() Elem { + return e.enclElem +} + +func (e *Common) setEnclosing(en Elem) { + e.enclElem = en +} + +// Escape characters that can be escaped without further escaping the string. +var charRe = regexp.MustCompile(`&#x[0-9a-fA-F]*;|\\u[0-9a-fA-F]{4}|\\U[0-9a-fA-F]{8}|\\x[0-9a-fA-F]{2}|\\[0-7]{3}|\\[abtnvfr]`) + +// replaceUnicode converts hexadecimal Unicode codepoint notations to a one-rune string. +// It assumes the input string is correctly formatted. +func replaceUnicode(s string) string { + if s[1] == '#' { + r, _ := strconv.ParseInt(s[3:len(s)-1], 16, 32) + return string(r) + } + r, _, _, _ := strconv.UnquoteChar(s, 0) + return string(r) +} diff --git a/vendor/golang.org/x/text/unicode/cldr/cldr.go b/vendor/golang.org/x/text/unicode/cldr/cldr.go new file mode 100644 index 000000000000..2197f8ac268e --- /dev/null +++ b/vendor/golang.org/x/text/unicode/cldr/cldr.go @@ -0,0 +1,130 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:generate go run makexml.go -output xml.go + +// Package cldr provides a parser for LDML and related XML formats. +// This package is intended to be used by the table generation tools +// for the various internationalization-related packages. +// As the XML types are generated from the CLDR DTD, and as the CLDR standard +// is periodically amended, this package may change considerably over time. +// This mostly means that data may appear and disappear between versions. +// That is, old code should keep compiling for newer versions, but data +// may have moved or changed. +// CLDR version 22 is the first version supported by this package. +// Older versions may not work. +package cldr // import "golang.org/x/text/unicode/cldr" + +import ( + "fmt" + "sort" +) + +// CLDR provides access to parsed data of the Unicode Common Locale Data Repository. +type CLDR struct { + parent map[string][]string + locale map[string]*LDML + resolved map[string]*LDML + bcp47 *LDMLBCP47 + supp *SupplementalData +} + +func makeCLDR() *CLDR { + return &CLDR{ + parent: make(map[string][]string), + locale: make(map[string]*LDML), + resolved: make(map[string]*LDML), + bcp47: &LDMLBCP47{}, + supp: &SupplementalData{}, + } +} + +// BCP47 returns the parsed BCP47 LDML data. If no such data was parsed, nil is returned. +func (cldr *CLDR) BCP47() *LDMLBCP47 { + return nil +} + +// Draft indicates the draft level of an element. +type Draft int + +const ( + Approved Draft = iota + Contributed + Provisional + Unconfirmed +) + +var drafts = []string{"unconfirmed", "provisional", "contributed", "approved", ""} + +// ParseDraft returns the Draft value corresponding to the given string. The +// empty string corresponds to Approved. +func ParseDraft(level string) (Draft, error) { + if level == "" { + return Approved, nil + } + for i, s := range drafts { + if level == s { + return Unconfirmed - Draft(i), nil + } + } + return Approved, fmt.Errorf("cldr: unknown draft level %q", level) +} + +func (d Draft) String() string { + return drafts[len(drafts)-1-int(d)] +} + +// SetDraftLevel sets which draft levels to include in the evaluated LDML. +// Any draft element for which the draft level is higher than lev will be excluded. +// If multiple draft levels are available for a single element, the one with the +// lowest draft level will be selected, unless preferDraft is true, in which case +// the highest draft will be chosen. +// It is assumed that the underlying LDML is canonicalized. +func (cldr *CLDR) SetDraftLevel(lev Draft, preferDraft bool) { + // TODO: implement + cldr.resolved = make(map[string]*LDML) +} + +// RawLDML returns the LDML XML for id in unresolved form. +// id must be one of the strings returned by Locales. +func (cldr *CLDR) RawLDML(loc string) *LDML { + return cldr.locale[loc] +} + +// LDML returns the fully resolved LDML XML for loc, which must be one of +// the strings returned by Locales. +func (cldr *CLDR) LDML(loc string) (*LDML, error) { + return cldr.resolve(loc) +} + +// Supplemental returns the parsed supplemental data. If no such data was parsed, +// nil is returned. +func (cldr *CLDR) Supplemental() *SupplementalData { + return cldr.supp +} + +// Locales returns the locales for which there exist files. +// Valid sublocales for which there is no file are not included. +// The root locale is always sorted first. +func (cldr *CLDR) Locales() []string { + loc := []string{"root"} + hasRoot := false + for l, _ := range cldr.locale { + if l == "root" { + hasRoot = true + continue + } + loc = append(loc, l) + } + sort.Strings(loc[1:]) + if !hasRoot { + return loc[1:] + } + return loc +} + +// Get fills in the fields of x based on the XPath path. +func Get(e Elem, path string) (res Elem, err error) { + return walkXPath(e, path) +} diff --git a/vendor/golang.org/x/text/unicode/cldr/collate.go b/vendor/golang.org/x/text/unicode/cldr/collate.go new file mode 100644 index 000000000000..80ee28d795e3 --- /dev/null +++ b/vendor/golang.org/x/text/unicode/cldr/collate.go @@ -0,0 +1,359 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cldr + +import ( + "bufio" + "encoding/xml" + "errors" + "fmt" + "strconv" + "strings" + "unicode" + "unicode/utf8" +) + +// RuleProcessor can be passed to Collator's Process method, which +// parses the rules and calls the respective method for each rule found. +type RuleProcessor interface { + Reset(anchor string, before int) error + Insert(level int, str, context, extend string) error + Index(id string) +} + +const ( + // cldrIndex is a Unicode-reserved sentinel value used to mark the start + // of a grouping within an index. + // We ignore any rule that starts with this rune. + // See http://unicode.org/reports/tr35/#Collation_Elements for details. + cldrIndex = "\uFDD0" + + // specialAnchor is the format in which to represent logical reset positions, + // such as "first tertiary ignorable". + specialAnchor = "<%s/>" +) + +// Process parses the rules for the tailorings of this collation +// and calls the respective methods of p for each rule found. +func (c Collation) Process(p RuleProcessor) (err error) { + if len(c.Cr) > 0 { + if len(c.Cr) > 1 { + return fmt.Errorf("multiple cr elements, want 0 or 1") + } + return processRules(p, c.Cr[0].Data()) + } + if c.Rules.Any != nil { + return c.processXML(p) + } + return errors.New("no tailoring data") +} + +// processRules parses rules in the Collation Rule Syntax defined in +// http://www.unicode.org/reports/tr35/tr35-collation.html#Collation_Tailorings. +func processRules(p RuleProcessor, s string) (err error) { + chk := func(s string, e error) string { + if err == nil { + err = e + } + return s + } + i := 0 // Save the line number for use after the loop. + scanner := bufio.NewScanner(strings.NewReader(s)) + for ; scanner.Scan() && err == nil; i++ { + for s := skipSpace(scanner.Text()); s != "" && s[0] != '#'; s = skipSpace(s) { + level := 5 + var ch byte + switch ch, s = s[0], s[1:]; ch { + case '&': // followed by or '[' ']' + if s = skipSpace(s); consume(&s, '[') { + s = chk(parseSpecialAnchor(p, s)) + } else { + s = chk(parseAnchor(p, 0, s)) + } + case '<': // sort relation '<'{1,4}, optionally followed by '*'. + for level = 1; consume(&s, '<'); level++ { + } + if level > 4 { + err = fmt.Errorf("level %d > 4", level) + } + fallthrough + case '=': // identity relation, optionally followed by *. + if consume(&s, '*') { + s = chk(parseSequence(p, level, s)) + } else { + s = chk(parseOrder(p, level, s)) + } + default: + chk("", fmt.Errorf("illegal operator %q", ch)) + break + } + } + } + if chk("", scanner.Err()); err != nil { + return fmt.Errorf("%d: %v", i, err) + } + return nil +} + +// parseSpecialAnchor parses the anchor syntax which is either of the form +// ['before' ] +// or +// [