pax_global_header00006660000000000000000000000064134621076110014513gustar00rootroot0000000000000052 comment=9eb7d9de6124fca123c23d970268aabed3a19417 golang-github-sap-go-hdb-0.14.1/000077500000000000000000000000001346210761100162445ustar00rootroot00000000000000golang-github-sap-go-hdb-0.14.1/.gitignore000066400000000000000000000004521346210761100202350ustar00rootroot00000000000000# Compiled Object files, Static and Dynamic libs (Shared Objects) *.o *.a *.so # Folders _obj _test # Architecture specific extensions/prefixes *.[568vq] [568vq].out *.cgo1.go *.cgo2.c _cgo_defun.c _cgo_gotypes.go _cgo_export.* _testmain.go *.exe *.test # Miscellaneous *.out *.prof todo*.* golang-github-sap-go-hdb-0.14.1/.gitlab-ci.yml000066400000000000000000000015661346210761100207100ustar00rootroot00000000000000variables: GIT_STRATEGY: clone GIT_CHECKOUT: "true" CI_DEBUG_TRACE: "false" stages: - build - test build: stage: build image: golang:latest script: - cd $CI_PROJECT_DIR/driver - go version - export GOARCH=amd64 - export GOOS=linux - go install - export GOARCH=amd64 - export GOOS=windows - go install .test_template: &test_definition stage: test script: - cd $CI_PROJECT_DIR/driver - go test -dsn $GO_HANA_DSN -test.v - cd $CI_PROJECT_DIR/internal/protocol - go test -test.v - cd $CI_PROJECT_DIR/internal/scanner - go test -test.v - cd $CI_PROJECT_DIR/internal/unicode/cesu8 - go test -test.v - cd $CI_PROJECT_DIR/driver - go test -dsn $GO_HANA_TLS_DSN -test.v golang_1_11_5: image: golang:1.11.5 <<: *test_definition golang_1_12_1: image: golang:1.12.1 <<: *test_definition golang-github-sap-go-hdb-0.14.1/LICENSE000066400000000000000000000260731346210761100172610ustar00rootroot00000000000000Apache 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.golang-github-sap-go-hdb-0.14.1/NOTICE000066400000000000000000000002301346210761100171430ustar00rootroot00000000000000SAP HANA Database driver for the Go Programming Language Copyright 2014 SAP SE This product includes software developed at SAP SE (http://www.sap.com).golang-github-sap-go-hdb-0.14.1/README.md000066400000000000000000000036431346210761100175310ustar00rootroot00000000000000go-hdb ====== [![GoDoc](https://godoc.org/github.com/SAP/go-hdb/driver?status.png)](https://godoc.org/github.com/SAP/go-hdb/driver) Go-hdb is a native Go (golang) HANA database driver for Go's sql package. It implements the SAP HANA SQL command network protocol: For the official SAP HANA client Go support (not this database driver) please see [SAP Help Portal](https://help.sap.com/viewer/0eec0d68141541d1b07893a39944924e/2.0.02/en-US/0ffbe86c9d9f44338441829c6bee15e6.html). ## Installation ``` go get github.com/SAP/go-hdb/driver ``` ## Building To build go-hdb you need to have a working Go environment with [version 1.11.5 or higher installed](https://golang.org/dl/). ## Documentation API documentation and documented examples can be found at . ## Tests To run the driver tests a HANA Database server is required. The test user must have privileges to create database schemas. ``` go test -dsn hdb://user:password@host:port ``` ## Features * Native Go implementation (no C libraries, CGO). * Go package compliant. * Support of database/sql/driver Execer and Queryer interface for parameter free statements and queries. * Support of bulk inserts. * Support of UTF-8 to / from CESU-8 encodings for HANA Unicode types. * Built-in support of HANA decimals as Go rational numbers . * Support of Large Object streaming. * Support of Stored Procedures with table output parameters. * Support of TLS TCP connections. * Support of little-endian (e.g. amd64) and big-endian architectures (e.g. s390x). * Support of [driver connector](https://golang.org/pkg/database/sql/driver/#Connector). ## Dependencies * ## Todo * Additional Authentication Methods (actually only basic authentication is supported). golang-github-sap-go-hdb-0.14.1/driver/000077500000000000000000000000001346210761100175375ustar00rootroot00000000000000golang-github-sap-go-hdb-0.14.1/driver/bulk_future_test.go000066400000000000000000000067531346210761100234670ustar00rootroot00000000000000// +build future /* 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" "fmt" "testing" ) const ( bulkSamples = 10000 ) // TestBulkInsert func TestBulkInsert(t *testing.T) { tmpTableName := Identifier("#tmpTable") db, err := sql.Open(DriverName, TestDSN) if err != nil { t.Fatal(err) } defer db.Close() //keep connection / hdb session for using local temporary tables tx, err := db.Begin() if err != nil { t.Fatal(err) } defer tx.Rollback() //cleanup if _, err := tx.Exec(fmt.Sprintf("create local temporary table %s (i integer)", tmpTableName)); err != nil { t.Fatalf("create table failed: %s", err) } stmt, err := tx.Prepare(fmt.Sprintf("insert into %s values (?)", tmpTableName)) if err != nil { t.Fatalf("prepare bulk insert failed: %s", err) } defer stmt.Close() prm := NoFlush for i := 0; i < bulkSamples; i++ { if i == (bulkSamples - 1) { prm = Flush } if _, err := stmt.Exec(i, prm); err != nil { t.Fatalf("insert failed: %s", err) } } i := 0 err = tx.QueryRow(fmt.Sprintf("select count(*) from %s", tmpTableName)).Scan(&i) if err != nil { t.Fatalf("select count failed: %s", err) } if i != bulkSamples { t.Fatalf("invalid number of records %d - %d expected", i, bulkSamples) } rows, err := tx.Query(fmt.Sprintf("select * from %s order by i", tmpTableName)) if err != nil { t.Fatal(err) } defer rows.Close() i = 0 for rows.Next() { var j int if err := rows.Scan(&j); err != nil { t.Fatal(err) } if j != i { t.Fatalf("value %d - expected %d", j, i) } i++ } if err := rows.Err(); err != nil { t.Fatal(err) } } // TestBulkInsertDuplicates func TestBulkInsertDuplicates(t *testing.T) { db, err := sql.Open(DriverName, TestDSN) if err != nil { t.Fatal(err) } defer db.Close() table := RandomIdentifier("bulkInsertDuplicates") if _, err := db.Exec(fmt.Sprintf("create table %s.%s (k integer primary key, v integer)", TestSchema, table)); err != nil { t.Fatalf("create table failed: %s", err) } stmt, err := db.Prepare(fmt.Sprintf("insert into %s.%s values (?,?)", TestSchema, table)) if err != nil { t.Fatalf("prepare bulk insert failed: %s", err) } defer stmt.Close() prm := NoFlush for i := 1; i < 4; i++ { if i == 3 { prm = Flush } if _, err := stmt.Exec(i, i, prm); err != nil { t.Fatalf("insert failed: %s", err) } } for i := 0; i < 4; i++ { if _, err := stmt.Exec(i, i, NoFlush); err != nil { t.Fatalf("insert failed: %s", err) } } _, err = stmt.Exec(5, 5) if err == nil { t.Fatal("error duplicate key expected") } dbError, ok := err.(Error) if !ok { t.Fatal("driver.Error expected") } // expect 3 errors for statement 1,2 and 3 if dbError.NumError() != 3 { t.Fatalf("number of errors: %d - %d expected", dbError.NumError(), 3) } stmtNo := []int{1, 2, 3} for i := 0; i < dbError.NumError(); i++ { dbError.SetIdx(i) if dbError.StmtNo() != stmtNo[i] { t.Fatalf("statement number: %d - %d expected", dbError.StmtNo(), stmtNo[i]) } } } golang-github-sap-go-hdb-0.14.1/driver/bulk_test.go000066400000000000000000000103441346210761100220640ustar00rootroot00000000000000// +build !future /* 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" "fmt" "testing" ) const ( bulkSamples = 10000 ) func TestCheckBulkInsert(t *testing.T) { var data = []struct { bulkSQL string sql string bulkInsert bool }{ {"bulk insert", "insert", true}, {" bulk insert ", "insert ", true}, {"BuLk iNsErT", "iNsErT", true}, {" bUlK InSeRt ", "InSeRt ", true}, {" bulkinsert ", " bulkinsert ", false}, {"bulk", "bulk", false}, {"insert", "insert", false}, } for i, d := range data { sql, bulkInsert := checkBulkInsert(d.bulkSQL) if sql != d.sql { t.Fatalf("test %d failed: bulk insert flag %t - %t expected", i, bulkInsert, d.bulkInsert) } if sql != d.sql { t.Fatalf("test %d failed: sql %s - %s expected", i, sql, d.sql) } } } // TestBulkInsert func TestBulkInsert(t *testing.T) { tmpTableName := Identifier("#tmpTable") db, err := sql.Open(DriverName, TestDSN) if err != nil { t.Fatal(err) } defer db.Close() //keep connection / hdb session for using local temporary tables tx, err := db.Begin() if err != nil { t.Fatal(err) } defer tx.Rollback() //cleanup if _, err := tx.Exec(fmt.Sprintf("create local temporary table %s (i integer)", tmpTableName)); err != nil { t.Fatalf("create table failed: %s", err) } stmt, err := tx.Prepare(fmt.Sprintf("bulk insert into %s values (?)", tmpTableName)) if err != nil { t.Fatalf("prepare bulk insert failed: %s", err) } defer stmt.Close() for i := 0; i < bulkSamples; i++ { if _, err := stmt.Exec(i); err != nil { t.Fatalf("insert failed: %s", err) } } // final flush if _, err := stmt.Exec(); err != nil { t.Fatalf("final insert (flush) failed: %s", err) } i := 0 err = tx.QueryRow(fmt.Sprintf("select count(*) from %s", tmpTableName)).Scan(&i) if err != nil { t.Fatalf("select count failed: %s", err) } if i != bulkSamples { t.Fatalf("invalid number of records %d - %d expected", i, bulkSamples) } rows, err := tx.Query(fmt.Sprintf("select * from %s order by i", tmpTableName)) if err != nil { t.Fatal(err) } defer rows.Close() i = 0 for rows.Next() { var j int if err := rows.Scan(&j); err != nil { t.Fatal(err) } if j != i { t.Fatalf("value %d - expected %d", j, i) } i++ } if err := rows.Err(); err != nil { t.Fatal(err) } } // TestBulkInsertDuplicates func TestBulkInsertDuplicates(t *testing.T) { db, err := sql.Open(DriverName, TestDSN) if err != nil { t.Fatal(err) } defer db.Close() table := RandomIdentifier("bulkInsertDuplicates") if _, err := db.Exec(fmt.Sprintf("create table %s.%s (k integer primary key, v integer)", TestSchema, table)); err != nil { t.Fatalf("create table failed: %s", err) } stmt, err := db.Prepare(fmt.Sprintf("bulk insert into %s.%s values (?,?)", TestSchema, table)) if err != nil { t.Fatalf("prepare bulk insert failed: %s", err) } defer stmt.Close() for i := 1; i < 4; i++ { if _, err := stmt.Exec(i, i); err != nil { t.Fatalf("insert failed: %s", err) } } if _, err := stmt.Exec(); err != nil { t.Fatalf("final insert (flush) failed: %s", err) } for i := 0; i < 5; i++ { if _, err := stmt.Exec(i, i); err != nil { t.Fatalf("insert failed: %s", err) } } _, err = stmt.Exec() if err == nil { t.Fatal("error duplicate key expected") } dbError, ok := err.(Error) if !ok { t.Fatal("driver.Error expected") } // expect 3 errors for statement 1,2 and 3 if dbError.NumError() != 3 { t.Fatalf("number of errors: %d - %d expected", dbError.NumError(), 3) } stmtNo := []int{1, 2, 3} for i := 0; i < dbError.NumError(); i++ { dbError.SetIdx(i) if dbError.StmtNo() != stmtNo[i] { t.Fatalf("statement number: %d - %d expected", dbError.StmtNo(), stmtNo[i]) } } } golang-github-sap-go-hdb-0.14.1/driver/bulkexample_future_test.go000066400000000000000000000026671346210761100250430ustar00rootroot00000000000000// +build future /* 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_test import ( "database/sql" "log" "github.com/SAP/go-hdb/driver" ) // ExampleBulkInsert inserts 1000 integer values into database table test. // Precondition: the test database table with one field of type integer must exist. // The insert SQL command is "bulk insert" instead of "insert". // After the insertion of the values a final stmt.Exec() without parameters must be executed. func Example_bulkInsert() { db, err := sql.Open(driver.DriverName, "hdb://user:password@host:port") if err != nil { log.Fatal(err) } defer db.Close() stmt, err := db.Prepare("bulk insert into test values (?)") // Prepare bulk query. if err != nil { log.Fatal(err) } defer stmt.Close() for i := 0; i < 1000; i++ { if _, err := stmt.Exec(i); err != nil { log.Fatal(err) } } // Call final stmt.Exec(). if _, err := stmt.Exec(); err != nil { log.Fatal(err) } } golang-github-sap-go-hdb-0.14.1/driver/bulkexample_test.go000066400000000000000000000026701346210761100234430ustar00rootroot00000000000000// +build !future /* 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_test import ( "database/sql" "log" "github.com/SAP/go-hdb/driver" ) // ExampleBulkInsert inserts 1000 integer values into database table test. // Precondition: the test database table with one field of type integer must exist. // The insert SQL command is "bulk insert" instead of "insert". // After the insertion of the values a final stmt.Exec() without parameters must be executed. func Example_bulkInsert() { db, err := sql.Open(driver.DriverName, "hdb://user:password@host:port") if err != nil { log.Fatal(err) } defer db.Close() stmt, err := db.Prepare("bulk insert into test values (?)") // Prepare bulk query. if err != nil { log.Fatal(err) } defer stmt.Close() for i := 0; i < 1000; i++ { if _, err := stmt.Exec(i); err != nil { log.Fatal(err) } } // Call final stmt.Exec(). if _, err := stmt.Exec(); err != nil { log.Fatal(err) } } golang-github-sap-go-hdb-0.14.1/driver/bytes.go000066400000000000000000000022141346210761100212130ustar00rootroot00000000000000/* 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 } golang-github-sap-go-hdb-0.14.1/driver/call_future_test.go000066400000000000000000000133241346210761100234350ustar00rootroot00000000000000// +build future /* 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 ( // "bytes" "database/sql" "fmt" // "log" "testing" ) func TestCallEcho(t *testing.T) { const procEcho = `create procedure %[1]s.%[2]s (in idata nvarchar(25), out odata nvarchar(25)) language SQLSCRIPT as begin odata := idata; end ` const txt = "Hello World!" db, err := sql.Open(DriverName, TestDSN) if err != nil { t.Fatal(err) } defer db.Close() procedure := RandomIdentifier("procEcho_") if _, err := db.Exec(fmt.Sprintf(procEcho, TestSchema, procedure)); err != nil { t.Fatal(err) } var out string _, err = db.Exec( fmt.Sprintf("call %s.%s(?, ?)", TestSchema, procedure), sql.Named("in", txt), sql.Named("out", sql.Out{Dest: &out}), ) if out != txt { t.Fatalf("value %s - expected %s", out, txt) } } //func TestCallBlobEcho(t *testing.T) { // const procBlobEcho = `create procedure %[1]s.%[2]s (in idata blob, out odata blob) //language SQLSCRIPT as //begin // odata := idata; //end //` // const txt = "Hello World!" // db, err := sql.Open(DriverName, TestDSN) // if err != nil { // t.Fatal(err) // } // defer db.Close() // procedure := RandomIdentifier("procBlobEcho_") // if _, err := db.Exec(fmt.Sprintf(procBlobEcho, TestSchema, procedure)); err != nil { // t.Fatal(err) // } // inlob := new(Lob) // inlob.SetReader(bytes.NewReader([]byte(txt))) // b := new(bytes.Buffer) // outlob := new(Lob) // outlob.SetWriter(b) // if err := db.QueryRow(fmt.Sprintf("call %s.%s(?, ?)", TestSchema, procedure), inlob).Scan(outlob); err != nil { // t.Fatal(err) // } // out := b.String() // if out != txt { // t.Fatalf("value %s - expected %s", out, txt) // } //} //type testTableData struct { // i int // x string //} //var testTableQuery1Data = []*testTableData{ // &testTableData{0, "A"}, // &testTableData{1, "B"}, // &testTableData{2, "C"}, // &testTableData{3, "D"}, // &testTableData{4, "E"}, //} //var testTableQuery2Data = []*testTableData{ // &testTableData{0, "A"}, // &testTableData{1, "B"}, // &testTableData{2, "C"}, // &testTableData{3, "D"}, // &testTableData{4, "E"}, // &testTableData{5, "F"}, // &testTableData{6, "G"}, // &testTableData{7, "H"}, // &testTableData{8, "I"}, // &testTableData{9, "J"}, //} //var testTableQuery3Data = []*testTableData{ // &testTableData{0, "A"}, // &testTableData{1, "B"}, // &testTableData{2, "C"}, // &testTableData{3, "D"}, // &testTableData{4, "E"}, // &testTableData{5, "F"}, // &testTableData{6, "G"}, // &testTableData{7, "H"}, // &testTableData{8, "I"}, // &testTableData{9, "J"}, // &testTableData{10, "K"}, // &testTableData{11, "L"}, // &testTableData{12, "M"}, // &testTableData{13, "N"}, // &testTableData{14, "O"}, //} //func checkTableQueryData(t *testing.T, db *sql.DB, query string, data []*testTableData) { // rows, err := db.Query(query) // if err != nil { // t.Fatal(err) // } // defer rows.Close() // j := 0 // for rows.Next() { // var i int // var x string // if err := rows.Scan(&i, &x); err != nil { // log.Fatal(err) // } // // log.Printf("i %d x %s", i, x) // if i != data[j].i { // t.Fatalf("value i %d - expected %d", i, data[j].i) // } // if x != data[j].x { // t.Fatalf("value x %s - expected %s", x, data[j].x) // } // j++ // } // if err := rows.Err(); err != nil { // log.Fatal(err) // } //} //func TestCallTableOut(t *testing.T) { // const procTableOut = `create procedure %[1]s.%[2]s (in i integer, out t1 %[1]s.%[3]s, out t2 %[1]s.%[3]s, out t3 %[1]s.%[3]s) //language SQLSCRIPT as //begin // create local temporary table #test like %[1]s.%[3]s; // insert into #test values(0, 'A'); // insert into #test values(1, 'B'); // insert into #test values(2, 'C'); // insert into #test values(3, 'D'); // insert into #test values(4, 'E'); // t1 = select * from #test; // insert into #test values(5, 'F'); // insert into #test values(6, 'G'); // insert into #test values(7, 'H'); // insert into #test values(8, 'I'); // insert into #test values(9, 'J'); // t2 = select * from #test; // insert into #test values(10, 'K'); // insert into #test values(11, 'L'); // insert into #test values(12, 'M'); // insert into #test values(13, 'N'); // insert into #test values(14, 'O'); // t3 = select * from #test; // drop table #test; //end //` // db, err := sql.Open(DriverName, TestDSN) // if err != nil { // t.Fatal(err) // } // defer db.Close() // tableType := RandomIdentifier("tt2_") // procedure := RandomIdentifier("procTableOut_") // if _, err := db.Exec(fmt.Sprintf("create type %s.%s as table (i integer, x varchar(10))", TestSchema, tableType)); err != nil { // t.Fatal(err) // } // if _, err := db.Exec(fmt.Sprintf(procTableOut, TestSchema, procedure, tableType)); err != nil { // t.Fatal(err) // } // var tableQuery1, tableQuery2, tableQuery3 string // rows, err := db.Query(fmt.Sprintf("call %s.%s(?, ?, ?, ?)", TestSchema, procedure), 1) // if err != nil { // t.Fatal(err) // } // defer rows.Close() // if !rows.Next() { // log.Fatal(rows.Err()) // } // if err := rows.Scan(&tableQuery1, &tableQuery2, &tableQuery3); err != nil { // log.Fatal(err) // } // checkTableQueryData(t, db, tableQuery1, testTableQuery1Data) // checkTableQueryData(t, db, tableQuery2, testTableQuery2Data) // checkTableQueryData(t, db, tableQuery3, testTableQuery3Data) //} golang-github-sap-go-hdb-0.14.1/driver/call_test.go000066400000000000000000000126171346210761100220470ustar00rootroot00000000000000// +build !future /* 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 ( "bytes" "database/sql" "fmt" "log" "testing" ) func TestCallEcho(t *testing.T) { const procEcho = `create procedure %[1]s.%[2]s (in idata nvarchar(25), out odata nvarchar(25)) language SQLSCRIPT as begin odata := idata; end ` const txt = "Hello World!" db, err := sql.Open(DriverName, TestDSN) if err != nil { t.Fatal(err) } defer db.Close() procedure := RandomIdentifier("procEcho_") if _, err := db.Exec(fmt.Sprintf(procEcho, TestSchema, procedure)); err != nil { t.Fatal(err) } var out string if err := db.QueryRow(fmt.Sprintf("call %s.%s(?, ?)", TestSchema, procedure), txt).Scan(&out); err != nil { t.Fatal(err) } if out != txt { t.Fatalf("value %s - expected %s", out, txt) } } func TestCallBlobEcho(t *testing.T) { const procBlobEcho = `create procedure %[1]s.%[2]s (in idata blob, out odata blob) language SQLSCRIPT as begin odata := idata; end ` const txt = "Hello World!" db, err := sql.Open(DriverName, TestDSN) if err != nil { t.Fatal(err) } defer db.Close() procedure := RandomIdentifier("procBlobEcho_") if _, err := db.Exec(fmt.Sprintf(procBlobEcho, TestSchema, procedure)); err != nil { t.Fatal(err) } inlob := new(Lob) inlob.SetReader(bytes.NewReader([]byte(txt))) b := new(bytes.Buffer) outlob := new(Lob) outlob.SetWriter(b) if err := db.QueryRow(fmt.Sprintf("call %s.%s(?, ?)", TestSchema, procedure), inlob).Scan(outlob); err != nil { t.Fatal(err) } out := b.String() if out != txt { t.Fatalf("value %s - expected %s", out, txt) } } type testTableData struct { i int x string } var testTableQuery1Data = []*testTableData{ &testTableData{0, "A"}, &testTableData{1, "B"}, &testTableData{2, "C"}, &testTableData{3, "D"}, &testTableData{4, "E"}, } var testTableQuery2Data = []*testTableData{ &testTableData{0, "A"}, &testTableData{1, "B"}, &testTableData{2, "C"}, &testTableData{3, "D"}, &testTableData{4, "E"}, &testTableData{5, "F"}, &testTableData{6, "G"}, &testTableData{7, "H"}, &testTableData{8, "I"}, &testTableData{9, "J"}, } var testTableQuery3Data = []*testTableData{ &testTableData{0, "A"}, &testTableData{1, "B"}, &testTableData{2, "C"}, &testTableData{3, "D"}, &testTableData{4, "E"}, &testTableData{5, "F"}, &testTableData{6, "G"}, &testTableData{7, "H"}, &testTableData{8, "I"}, &testTableData{9, "J"}, &testTableData{10, "K"}, &testTableData{11, "L"}, &testTableData{12, "M"}, &testTableData{13, "N"}, &testTableData{14, "O"}, } func checkTableQueryData(t *testing.T, db *sql.DB, query string, data []*testTableData) { rows, err := db.Query(query) if err != nil { t.Fatal(err) } defer rows.Close() j := 0 for rows.Next() { var i int var x string if err := rows.Scan(&i, &x); err != nil { log.Fatal(err) } // log.Printf("i %d x %s", i, x) if i != data[j].i { t.Fatalf("value i %d - expected %d", i, data[j].i) } if x != data[j].x { t.Fatalf("value x %s - expected %s", x, data[j].x) } j++ } if err := rows.Err(); err != nil { log.Fatal(err) } } func TestCallTableOut(t *testing.T) { const procTableOut = `create procedure %[1]s.%[2]s (in i integer, out t1 %[1]s.%[3]s, out t2 %[1]s.%[3]s, out t3 %[1]s.%[3]s) language SQLSCRIPT as begin create local temporary table #test like %[1]s.%[3]s; insert into #test values(0, 'A'); insert into #test values(1, 'B'); insert into #test values(2, 'C'); insert into #test values(3, 'D'); insert into #test values(4, 'E'); t1 = select * from #test; insert into #test values(5, 'F'); insert into #test values(6, 'G'); insert into #test values(7, 'H'); insert into #test values(8, 'I'); insert into #test values(9, 'J'); t2 = select * from #test; insert into #test values(10, 'K'); insert into #test values(11, 'L'); insert into #test values(12, 'M'); insert into #test values(13, 'N'); insert into #test values(14, 'O'); t3 = select * from #test; drop table #test; end ` db, err := sql.Open(DriverName, TestDSN) if err != nil { t.Fatal(err) } defer db.Close() tableType := RandomIdentifier("tt2_") procedure := RandomIdentifier("procTableOut_") if _, err := db.Exec(fmt.Sprintf("create type %s.%s as table (i integer, x varchar(10))", TestSchema, tableType)); err != nil { t.Fatal(err) } if _, err := db.Exec(fmt.Sprintf(procTableOut, TestSchema, procedure, tableType)); err != nil { t.Fatal(err) } var tableQuery1, tableQuery2, tableQuery3 string rows, err := db.Query(fmt.Sprintf("call %s.%s(?, ?, ?, ?)", TestSchema, procedure), 1) if err != nil { t.Fatal(err) } defer rows.Close() if !rows.Next() { log.Fatal(rows.Err()) } if err := rows.Scan(&tableQuery1, &tableQuery2, &tableQuery3); err != nil { log.Fatal(err) } checkTableQueryData(t, db, tableQuery1, testTableQuery1Data) checkTableQueryData(t, db, tableQuery2, testTableQuery2Data) checkTableQueryData(t, db, tableQuery3, testTableQuery3Data) } golang-github-sap-go-hdb-0.14.1/driver/callexample_test.go000066400000000000000000000067401346210761100234230ustar00rootroot00000000000000/* 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" "fmt" "log" ) /* ExampleCallSimpleOut creates a stored procedure with one output parameter and executes it. Stored procedures with output parameters must be executed by sql.Query or sql.QueryRow. For variables TestDSN and TestSchema see main_test.go. */ func Example_callSimpleOut() { const procOut = `create procedure %s.%s (out message nvarchar(1024)) language SQLSCRIPT as begin message := 'Hello World!'; end ` db, err := sql.Open(DriverName, TestDSN) if err != nil { log.Fatal(err) } defer db.Close() procedure := RandomIdentifier("procOut_") if _, err := db.Exec(fmt.Sprintf(procOut, TestSchema, procedure)); err != nil { // Create stored procedure. log.Fatal(err) } var out string if err := db.QueryRow(fmt.Sprintf("call %s.%s(?)", TestSchema, procedure)).Scan(&out); err != nil { log.Fatal(err) } fmt.Print(out) // output: Hello World! } /* ExampleCallTableOut creates a stored procedure with one table output parameter and executes it. Stored procedures with table output parameters must be executed by sql.Query as sql.QueryRow will close the query after execution and prevent querying output table values. The scan type of a table output parameter is a string containing an opaque value to query table output values by standard sql.Query or sql.QueryRow methods. For variables TestDSN and TestSchema see main_test.go. */ func Example_callTableOut() { const procTable = `create procedure %[1]s.%[2]s (out t %[1]s.%[3]s) language SQLSCRIPT as begin create local temporary table #test like %[1]s.%[3]s; insert into #test values('Hello, 世界'); insert into #test values('SAP HANA'); insert into #test values('Go driver'); t = select * from #test; drop table #test; end ` db, err := sql.Open(DriverName, TestDSN) if err != nil { log.Fatal(err) } defer db.Close() tableType := RandomIdentifier("TableType_") procedure := RandomIdentifier("ProcTable_") if _, err := db.Exec(fmt.Sprintf("create type %s.%s as table (x nvarchar(256))", TestSchema, tableType)); err != nil { // Create table type. log.Fatal(err) } if _, err := db.Exec(fmt.Sprintf(procTable, TestSchema, procedure, tableType)); err != nil { // Create stored procedure. log.Fatal(err) } var tableQuery string // Scan variable of table output parameter. // Query stored procedure. rows, err := db.Query(fmt.Sprintf("call %s.%s(?)", TestSchema, procedure)) if err != nil { log.Fatal(err) } defer rows.Close() if !rows.Next() { log.Fatal(rows.Err()) } if err := rows.Scan(&tableQuery); err != nil { log.Fatal(err) } // Query stored procedure output table. tableRows, err := db.Query(tableQuery) if err != nil { log.Fatal(err) } defer tableRows.Close() for tableRows.Next() { var x string if err := tableRows.Scan(&x); err != nil { log.Fatal(err) } fmt.Println(x) } if err := tableRows.Err(); err != nil { log.Fatal(err) } // output: Hello, 世界 // SAP HANA // Go driver } golang-github-sap-go-hdb-0.14.1/driver/columntype_test.go000066400000000000000000000160321346210761100233260ustar00rootroot00000000000000/* 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 ( "bytes" "database/sql" "fmt" "math/big" "reflect" "strings" "testing" "time" ) type testColumnType struct { sqlType string length int64 varLength bool sizeable bool decimalType bool typeName string precision int64 scale int64 nullable bool scanType reflect.Type value interface{} } func dataType(dt string) string { if driverDataFormatVersion == 1 { switch dt { case "DAYDATE": return "DATE" case "SECONDTIME": return "TIME" case "LONGDATE", "SECONDDATE": return "TIMESTAMP" } } return dt } func TestColumnType(t *testing.T) { var ( testTime = time.Now() testDecimal = (*Decimal)(big.NewRat(1, 1)) testString = "HDB column type" testBinary = []byte{0x00, 0x01, 0x02} testBuffer = bytes.NewBuffer(testBinary) testLob = new(Lob) ) testLob.SetReader(testBuffer) var testColumnTypeData = []testColumnType{ {"tinyint", 0, false, false, false, "TINYINT", 0, 0, true, scanTypeTinyint, 1}, {"smallint", 0, false, false, false, "SMALLINT", 0, 0, true, scanTypeSmallint, 42}, {"integer", 0, false, false, false, "INTEGER", 0, 0, true, scanTypeInteger, 4711}, {"bigint", 0, false, false, false, "BIGINT", 0, 0, true, scanTypeBigint, 68000}, {"decimal", 0, false, false, true, "DECIMAL", 34, 32767, true, scanTypeDecimal, testDecimal}, {"real", 0, false, false, false, "REAL", 0, 0, true, scanTypeReal, 1.0}, {"double", 0, false, false, false, "DOUBLE", 0, 0, true, scanTypeDouble, 3.14}, {"char", 30, true, false, false, "CHAR", 0, 0, true, scanTypeString, testString}, {"varchar", 30, true, false, false, "VARCHAR", 0, 0, true, scanTypeString, testString}, {"nchar", 20, true, false, false, "NCHAR", 0, 0, true, scanTypeString, testString}, {"nvarchar", 20, true, false, false, "NVARCHAR", 0, 0, true, scanTypeString, testString}, {"binary", 10, true, false, false, "BINARY", 0, 0, true, scanTypeBytes, testBinary}, {"varbinary", 10, true, false, false, "VARBINARY", 0, 0, true, scanTypeBytes, testBinary}, {"date", 0, false, false, false, dataType("DAYDATE"), 0, 0, true, scanTypeTime, testTime}, {"time", 0, false, false, false, dataType("SECONDTIME"), 0, 0, true, scanTypeTime, testTime}, {"timestamp", 0, false, false, false, dataType("LONGDATE"), 0, 0, true, scanTypeTime, testTime}, {"clob", 0, false, false, false, "CLOB", 0, 0, true, scanTypeLob, testLob}, {"nclob", 0, false, false, false, "NCLOB", 0, 0, true, scanTypeLob, testLob}, {"blob", 0, false, false, false, "BLOB", 0, 0, true, scanTypeLob, testLob}, {"boolean", 0, false, false, false, "TINYINT", 0, 0, true, scanTypeTinyint, false}, // hdb gives TINYINT back - not BOOLEAN {"smalldecimal", 0, false, false, true, "DECIMAL", 16, 32767, true, scanTypeDecimal, testDecimal}, // hdb gives DECIMAL back - not SMALLDECIMAL //{"text", 0, false, false, "NCLOB", 0, 0, true, testLob}, // hdb gives NCLOB back - not TEXT //{"shorttext", 15, true, false, "NVARCHAR", 0, 0, true, testString}, // hdb gives NVARCHAR back - not SHORTTEXT //{"alphanum", 15, true, false, "NVARCHAR", 0, 0, true, testString}, // hdb gives NVARCHAR back - not ALPHANUM {"longdate", 0, false, false, false, dataType("LONGDATE"), 0, 0, true, scanTypeTime, testTime}, {"seconddate", 0, false, false, false, dataType("SECONDDATE"), 0, 0, true, scanTypeTime, testTime}, {"daydate", 0, false, false, false, dataType("DAYDATE"), 0, 0, true, scanTypeTime, testTime}, {"secondtime", 0, false, false, false, dataType("SECONDTIME"), 0, 0, true, scanTypeTime, testTime}, // not nullable {"tinyint", 0, false, false, false, "TINYINT", 0, 0, false, scanTypeTinyint, 42}, {"nvarchar", 25, true, false, false, "NVARCHAR", 0, 0, false, scanTypeString, testString}, } // text is only supported for column table var createSQL bytes.Buffer table := RandomIdentifier("testColumnType_") createSQL.WriteString(fmt.Sprintf("create column table %s.%s (", TestSchema, table)) // some data types are only valid for column tables for i, td := range testColumnTypeData { if i != 0 { createSQL.WriteString(",") } createSQL.WriteString(fmt.Sprintf("X%d %s", i, td.sqlType)) if td.length != 0 { createSQL.WriteString(fmt.Sprintf("(%d)", td.length)) } if !td.nullable { createSQL.WriteString(" not null") } } createSQL.WriteString(")") db, err := sql.Open(DriverName, TestDSN) if err != nil { t.Fatal(err) } defer db.Close() if _, err := db.Exec(createSQL.String()); err != nil { t.Fatal(err) } args := make([]interface{}, len(testColumnTypeData)) for i, td := range testColumnTypeData { args[i] = td.value } prms := strings.Repeat("?,", len(testColumnTypeData)-1) + "?" // use trancactions: // SQL Error 596 - LOB streaming is not permitted in auto-commit mode tx, err := db.Begin() if err != nil { t.Fatal(err) } if _, err := tx.Exec(fmt.Sprintf("insert into %s.%s values (%s)", TestSchema, table, prms), args...); err != nil { t.Fatal(err) } if err := tx.Commit(); err != nil { t.Fatal(err) } rows, err := db.Query(fmt.Sprintf("select * from %s.%s", TestSchema, table)) if err != nil { t.Fatal(err) } defer rows.Close() cts, err := rows.ColumnTypes() if err != nil { t.Fatal(err) } for i, td := range testColumnTypeData { ct := cts[i] if td.typeName != ct.DatabaseTypeName() { t.Fatalf("index %d sql type %s type name %s - expected %s", i, td.sqlType, ct.DatabaseTypeName(), td.typeName) } length, ok := ct.Length() if td.varLength != ok { t.Fatalf("index %d sql type %s variable length %t - expected %t", i, td.sqlType, ok, td.varLength) } if td.length != length { t.Fatalf("index %d sql type %s length %d - expected %d", i, td.sqlType, length, td.length) } precision, scale, ok := ct.DecimalSize() if td.decimalType != ok { t.Fatalf("index %d sql type %s decimal %t - expected %t", i, td.sqlType, ok, td.decimalType) } if td.precision != precision { t.Fatalf("index %d sql type %s precision %d - expected %d", i, td.sqlType, precision, td.precision) } if td.scale != scale { t.Fatalf("index %d sql type %s scale %d - expected %d", i, td.sqlType, scale, td.scale) } nullable, ok := ct.Nullable() if !ok { t.Fatalf("index %d sql type %s - nullable info is expected to be provided", i, td.sqlType) } if td.nullable != nullable { t.Fatalf("index %d sql type %s nullable %t - expected %t", i, td.sqlType, nullable, td.nullable) } if ct.ScanType() != td.scanType { t.Fatalf("index %d sql type %s scan type %v - expected %v", i, td.sqlType, ct.ScanType(), td.scanType) } } } golang-github-sap-go-hdb-0.14.1/driver/conn_test.go000066400000000000000000000017231346210761100220650ustar00rootroot00000000000000/* 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" "testing" ) func TestConnection(t *testing.T) { db, err := sql.Open(DriverName, TestDSN) if err != nil { t.Fatal(err) } defer db.Close() var dummy string err = db.QueryRow("select * from dummy").Scan(&dummy) switch { case err == sql.ErrNoRows: t.Fatal(err) case err != nil: t.Fatal(err) } if dummy != "X" { t.Fatalf("dummy is %s - expected %s", dummy, "X") } } golang-github-sap-go-hdb-0.14.1/driver/connector.go000066400000000000000000000155501346210761100220660ustar00rootroot00000000000000/* 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 ( "context" "crypto/tls" "crypto/x509" "database/sql/driver" "fmt" "io/ioutil" "net/url" "strconv" "sync" ) /* SessionVaribles maps session variables to their values. All defined session variables will be set once after a database connection is opened. */ type SessionVariables map[string]string /* A Connector represents a hdb driver in a fixed configuration. A Connector can be passed to sql.OpenDB (starting from go 1.10) allowing users to bypass a string based data source name. */ type Connector struct { mu sync.RWMutex host, username, password string locale string bufferSize, fetchSize, timeout int tlsConfig *tls.Config sessionVariables SessionVariables } func newConnector() *Connector { return &Connector{ fetchSize: DefaultFetchSize, timeout: DefaultTimeout, } } // NewBasicAuthConnector creates a connector for basic authentication. func NewBasicAuthConnector(host, username, password string) *Connector { c := newConnector() c.host = host c.username = username c.password = password return c } // NewDSNConnector creates a connector from a data source name. func NewDSNConnector(dsn string) (*Connector, error) { c := newConnector() url, err := url.Parse(dsn) if err != nil { return nil, err } c.host = url.Host if url.User != nil { c.username = url.User.Username() c.password, _ = url.User.Password() } var certPool *x509.CertPool for k, v := range url.Query() { switch k { default: return nil, fmt.Errorf("URL parameter %s is not supported", k) case DSNFetchSize: if len(v) == 0 { continue } fetchSize, err := strconv.Atoi(v[0]) if err != nil { return nil, fmt.Errorf("failed to parse fetchSize: %s", v[0]) } if fetchSize < minFetchSize { c.fetchSize = minFetchSize } else { c.fetchSize = fetchSize } case DSNTimeout: if len(v) == 0 { continue } timeout, err := strconv.Atoi(v[0]) if err != nil { return nil, fmt.Errorf("failed to parse timeout: %s", v[0]) } if timeout < minTimeout { c.timeout = minTimeout } else { c.timeout = timeout } case DSNLocale: if len(v) == 0 { continue } c.locale = v[0] case DSNTLSServerName: if len(v) == 0 { continue } if c.tlsConfig == nil { c.tlsConfig = &tls.Config{} } c.tlsConfig.ServerName = v[0] case DSNTLSInsecureSkipVerify: if len(v) == 0 { continue } var err error b := true if v[0] != "" { b, err = strconv.ParseBool(v[0]) if err != nil { return nil, fmt.Errorf("failed to parse InsecureSkipVerify (bool): %s", v[0]) } } if c.tlsConfig == nil { c.tlsConfig = &tls.Config{} } c.tlsConfig.InsecureSkipVerify = b case DSNTLSRootCAFile: for _, fn := range v { rootPEM, err := ioutil.ReadFile(fn) if err != nil { return nil, err } if certPool == nil { certPool = x509.NewCertPool() } if ok := certPool.AppendCertsFromPEM(rootPEM); !ok { return nil, fmt.Errorf("failed to parse root certificate - filename: %s", fn) } } if certPool != nil { if c.tlsConfig == nil { c.tlsConfig = &tls.Config{} } c.tlsConfig.RootCAs = certPool } } } return c, nil } // Host returns the host of the connector. func (c *Connector) Host() string { return c.host } // Username returns the username of the connector. func (c *Connector) Username() string { return c.username } // Password returns the password of the connector. func (c *Connector) Password() string { return c.password } // Locale returns the locale of the connector. func (c *Connector) Locale() string { c.mu.RLock() defer c.mu.RUnlock() return c.locale } /* SetLocale sets the locale of the connector. For more information please see DSNLocale. */ func (c *Connector) SetLocale(locale string) { c.mu.Lock() c.locale = locale c.mu.Unlock() } // FetchSize returns the fetchSize of the connector. func (c *Connector) FetchSize() int { c.mu.RLock() defer c.mu.RUnlock() return c.fetchSize } /* SetFetchSize sets the fetchSize of the connector. For more information please see DSNFetchSize. */ func (c *Connector) SetFetchSize(fetchSize int) error { c.mu.Lock() defer c.mu.Unlock() if fetchSize < minFetchSize { fetchSize = minFetchSize } c.fetchSize = fetchSize return nil } // Timeout returns the timeout of the connector. func (c *Connector) Timeout() int { c.mu.RLock() defer c.mu.RUnlock() return c.timeout } /* SetTimeout sets the timeout of the connector. For more information please see DSNTimeout. */ func (c *Connector) SetTimeout(timeout int) error { c.mu.Lock() defer c.mu.Unlock() if timeout < minTimeout { timeout = minTimeout } c.timeout = timeout return nil } // TLSConfig returns the TLS configuration of the connector. func (c *Connector) TLSConfig() *tls.Config { c.mu.RLock() defer c.mu.RUnlock() return c.tlsConfig } // SetTLSConfig sets the TLS configuration of the connector. func (c *Connector) SetTLSConfig(tlsConfig *tls.Config) error { c.mu.Lock() defer c.mu.Unlock() c.tlsConfig = tlsConfig return nil } // SessionVariables returns the session variables stored in connector. func (c *Connector) SessionVariables() SessionVariables { c.mu.RLock() defer c.mu.RUnlock() return c.sessionVariables } // SetSessionVariables sets the session varibles of the connector. func (c *Connector) SetSessionVariables(sessionVariables SessionVariables) error { c.mu.Lock() defer c.mu.Unlock() c.sessionVariables = sessionVariables return nil } // BasicAuthDSN return the connector DSN for basic authentication. func (c *Connector) BasicAuthDSN() string { values := url.Values{} if c.locale != "" { values.Set(DSNLocale, c.locale) } if c.fetchSize != 0 { values.Set(DSNFetchSize, fmt.Sprintf("%d", c.fetchSize)) } if c.timeout != 0 { values.Set(DSNTimeout, fmt.Sprintf("%d", c.timeout)) } return (&url.URL{ Scheme: DriverName, User: url.UserPassword(c.username, c.password), Host: c.host, RawQuery: values.Encode(), }).String() } // Connect implements the database/sql/driver/Connector interface. func (c *Connector) Connect(ctx context.Context) (driver.Conn, error) { return newConn(ctx, c) } // Driver implements the database/sql/driver/Connector interface. func (c *Connector) Driver() driver.Driver { return drv } golang-github-sap-go-hdb-0.14.1/driver/connector_test.go000066400000000000000000000040541346210761100231220ustar00rootroot00000000000000/* 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_test import ( "database/sql" "database/sql/driver" "fmt" "testing" goHdbDriver "github.com/SAP/go-hdb/driver" ) func TestConnector(t *testing.T) { dsnConnector, err := goHdbDriver.NewDSNConnector(goHdbDriver.TestDSN) if err != nil { t.Fatal(err) } testConnector(t, dsnConnector) basicAuthConnector := goHdbDriver.NewBasicAuthConnector(dsnConnector.Host(), dsnConnector.Username(), dsnConnector.Password()) testConnector(t, basicAuthConnector) } func testConnector(t *testing.T, connector driver.Connector) { db := sql.OpenDB(connector) defer db.Close() var dummy string err := db.QueryRow("select * from dummy").Scan(&dummy) switch { case err == sql.ErrNoRows: t.Fatal(err) case err != nil: t.Fatal(err) } if dummy != "X" { t.Fatalf("dummy is %s - expected %s", dummy, "X") } } func TestSessionVariables(t *testing.T) { ctor, err := goHdbDriver.NewDSNConnector(goHdbDriver.TestDSN) if err != nil { t.Fatal(err) } // set session variables sv := goHdbDriver.SessionVariables{"k1": "v1", "k2": "v2", "k3": "v3"} if err := ctor.SetSessionVariables(sv); err != nil { t.Fatal(err) } // check session variables db := sql.OpenDB(ctor) defer db.Close() var val string for k, v := range sv { err := db.QueryRow(fmt.Sprintf("select session_context('%s') from dummy", k)).Scan(&val) switch { case err == sql.ErrNoRows: t.Fatal(err) case err != nil: t.Fatal(err) } if val != v { t.Fatalf("session variable value for %s is %s - expected %s", k, val, v) } } } golang-github-sap-go-hdb-0.14.1/driver/connectorexample_test.go000066400000000000000000000016771346210761100245060ustar00rootroot00000000000000/* 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_test import ( "database/sql" "log" "github.com/SAP/go-hdb/driver" ) // ExampleConnector shows how to open a database with the help of a connector. func ExampleConnector() { connector := driver.NewBasicAuthConnector("host:port", "username", "password") connector.SetTimeout(60) db := sql.OpenDB(connector) defer db.Close() if err := db.Ping(); err != nil { log.Fatal(err) } } golang-github-sap-go-hdb-0.14.1/driver/converter.go000066400000000000000000000166461346210761100221120ustar00rootroot00000000000000/* 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 ) // ErrIntegerOutOfRange means that an integer exceeds the size of the hdb integer field. var ErrIntegerOutOfRange = errors.New("integer out of range error") // ErrFloatOutOfRange 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 checkNamedValue(prmFieldSet *p.ParameterFieldSet, nv *driver.NamedValue) error { idx := nv.Ordinal - 1 if idx >= prmFieldSet.NumInputField() { return nil } f := prmFieldSet.Field(idx) dt := f.TypeCode().DataType() value, err := convertNamedValue(idx, f, dt, nv.Value) if err != nil { return err } nv.Value = value return nil } func convertNamedValue(idx int, f *p.ParameterField, dt p.DataType, v driver.Value) (driver.Value, error) { var err error // let fields with own Value converter convert themselves first (e.g. NullInt64, ...) if _, ok := v.(driver.Valuer); ok { if v, err = driver.DefaultParameterConverter.ConvertValue(v); err != nil { return nil, err } } switch dt { default: return nil, fmt.Errorf("convert named value datatype error: %[1]d - %[1]s", dt) case p.DtTinyint: return convertNvInteger(v, minTinyint, maxTinyint) case p.DtSmallint: return convertNvInteger(v, minSmallint, maxSmallint) case p.DtInteger: return convertNvInteger(v, minInteger, maxInteger) case p.DtBigint: return convertNvInteger(v, minBigint, maxBigint) case p.DtReal: return convertNvFloat(v, maxReal) case p.DtDouble: return convertNvFloat(v, maxDouble) case p.DtTime: return convertNvTime(v) case p.DtDecimal: return convertNvDecimal(v) case p.DtString: return convertNvString(v) case p.DtBytes: return convertNvBytes(v) case p.DtLob: return convertNvLob(idx, f, v) } } // integer types func convertNvInteger(v interface{}, min, max int64) (driver.Value, error) { if v == nil { return v, nil } rv := reflect.ValueOf(v) switch rv.Kind() { // bool is represented in HDB as tinyint case reflect.Bool: return rv.Bool(), nil case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: i64 := rv.Int() if i64 > max || i64 < min { return nil, ErrIntegerOutOfRange } return i64, nil case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: u64 := rv.Uint() if u64 > uint64(max) { return nil, ErrIntegerOutOfRange } return int64(u64), nil case reflect.Ptr: // indirect pointers if rv.IsNil() { return nil, nil } return convertNvInteger(rv.Elem().Interface(), min, max) } return nil, fmt.Errorf("unsupported integer conversion type error %[1]T %[1]v", v) } // float types func convertNvFloat(v interface{}, max float64) (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) > max { return nil, ErrFloatOutOfRange } return f64, nil case reflect.Ptr: // indirect pointers if rv.IsNil() { return nil, nil } return convertNvFloat(rv.Elem().Interface(), max) } return nil, fmt.Errorf("unsupported float conversion type error %[1]T %[1]v", v) } // time func convertNvTime(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 convertNvTime(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 %[1]T %[1]v", v) } // decimal func convertNvDecimal(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 %[1]T %[1]v", v) } // string func convertNvString(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 convertNvString(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 %[1]T %[1]v", v) } // bytes func convertNvBytes(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 convertNvBytes(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 %[1]T %[1]v", v) } // Lob func convertNvLob(idx int, f *p.ParameterField, v interface{}) (driver.Value, error) { if v == nil { return v, nil } switch v := v.(type) { case Lob: if v.rd == nil { return nil, fmt.Errorf("lob error: initial reader %[1]T %[1]v", v) } f.SetLobReader(v.rd) return fmt.Sprintf(" size 40 bytes // nvarchar: CESU-8 6 bytes per char -> hdb counts 2 chars per 6 byte encoding -> size 20 bytes "𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞", "𝄞𝄞aa", "€€", "𝄞𝄞€€", "𝄞𝄞𝄞€€", "aaaaaaaaaa", sql.NullString{Valid: false, String: "Hello HDB"}, sql.NullString{Valid: true, String: "Hello HDB"}, } /* using unicode (CESU-8) data for char HDB - successful insert into table - but query table returns SQL HdbError 7 - feature not supported: invalid character encoding: ... --> use ASCII test data only surprisingly: varchar works with unicode characters */ func TestChar(t *testing.T) { testDatatype(t, "char", 40, true, testStringDataASCII...) } func TestVarchar(t *testing.T) { testDatatype(t, "varchar", 40, false, testStringData...) } func TestNChar(t *testing.T) { testDatatype(t, "nchar", 20, true, testStringData...) } func TestNVarchar(t *testing.T) { testDatatype(t, "nvarchar", 20, false, testStringData...) } var testBinaryData = []interface{}{ []byte("Hello HDB"), []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19}, []byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0xff}, NullBytes{Valid: false, Bytes: []byte("Hello HDB")}, NullBytes{Valid: true, Bytes: []byte("Hello HDB")}, } func TestBinary(t *testing.T) { testDatatype(t, "binary", 20, true, testBinaryData...) } func TestVarbinary(t *testing.T) { testDatatype(t, "varbinary", 20, false, testBinaryData...) } var testTimeData = []interface{}{ time.Now(), NullTime{Valid: false, Time: time.Now()}, NullTime{Valid: true, Time: time.Now()}, } func TestDate(t *testing.T) { testDatatype(t, "date", 0, true, testTimeData...) } func TestTime(t *testing.T) { testDatatype(t, "time", 0, true, testTimeData...) } func TestTimestamp(t *testing.T) { testDatatype(t, "timestamp", 0, true, testTimeData...) } func TestLongdate(t *testing.T) { testDatatype(t, "longdate", 0, true, testTimeData...) } func TestSeconddate(t *testing.T) { testDatatype(t, "seconddate", 0, true, testTimeData...) } func TestDaydate(t *testing.T) { testDatatype(t, "daydate", 0, true, testTimeData...) } func TestSecondtime(t *testing.T) { testDatatype(t, "secondtime", 0, true, testTimeData...) } var testDecimalData = []interface{}{ (*Decimal)(big.NewRat(0, 1)), (*Decimal)(big.NewRat(1, 1)), (*Decimal)(big.NewRat(-1, 1)), (*Decimal)(big.NewRat(10, 1)), (*Decimal)(big.NewRat(1000, 1)), (*Decimal)(big.NewRat(1, 10)), (*Decimal)(big.NewRat(-1, 10)), (*Decimal)(big.NewRat(1, 1000)), (*Decimal)(new(big.Rat).SetInt(maxDecimal)), NullDecimal{Valid: false, Decimal: (*Decimal)(big.NewRat(1, 1))}, NullDecimal{Valid: true, Decimal: (*Decimal)(big.NewRat(1, 1))}, } func TestDecimal(t *testing.T) { testDatatype(t, "decimal", 0, true, testDecimalData...) } func TestBoolean(t *testing.T) { testDatatype(t, "boolean", 0, true, true, false, sql.NullBool{Valid: false, Bool: true}, sql.NullBool{Valid: true, Bool: false}, ) } func TestClob(t *testing.T) { testInitLobFiles(t) testLobDataASCII := make([]interface{}, 0, len(testLobFiles)) first := true for _, f := range testLobFiles { if f.isASCII { if first { testLobDataASCII = append(testLobDataASCII, NullLob{Valid: false, Lob: &Lob{rd: bytes.NewReader(f.content)}}) testLobDataASCII = append(testLobDataASCII, NullLob{Valid: true, Lob: &Lob{rd: bytes.NewReader(f.content)}}) first = false } testLobDataASCII = append(testLobDataASCII, Lob{rd: bytes.NewReader(f.content)}) } } testDatatype(t, "clob", 0, true, testLobDataASCII...) } func TestNclob(t *testing.T) { testInitLobFiles(t) testLobData := make([]interface{}, 0, len(testLobFiles)+2) for i, f := range testLobFiles { if i == 0 { testLobData = append(testLobData, NullLob{Valid: false, Lob: &Lob{rd: bytes.NewReader(f.content)}}) testLobData = append(testLobData, NullLob{Valid: true, Lob: &Lob{rd: bytes.NewReader(f.content)}}) } testLobData = append(testLobData, Lob{rd: bytes.NewReader(f.content)}) } testDatatype(t, "nclob", 0, true, testLobData...) } func TestBlob(t *testing.T) { testInitLobFiles(t) testLobData := make([]interface{}, 0, len(testLobFiles)+2) for i, f := range testLobFiles { if i == 0 { testLobData = append(testLobData, NullLob{Valid: false, Lob: &Lob{rd: bytes.NewReader(f.content)}}) testLobData = append(testLobData, NullLob{Valid: true, Lob: &Lob{rd: bytes.NewReader(f.content)}}) } testLobData = append(testLobData, Lob{rd: bytes.NewReader(f.content)}) } testDatatype(t, "blob", 0, true, testLobData...) } // func testDatatype(t *testing.T, dataType string, dataSize int, fixedSize bool, testData ...interface{}) { db, err := sql.Open(DriverName, TestDSN) if err != nil { t.Fatal(err) } defer db.Close() table := RandomIdentifier(fmt.Sprintf("%s_", dataType)) if dataSize == 0 { if _, err := db.Exec(fmt.Sprintf("create table %s.%s (i integer, x %s)", TestSchema, table, dataType)); err != nil { t.Fatal(err) } } else { if _, err := db.Exec(fmt.Sprintf("create table %s.%s (i integer, x %s(%d))", TestSchema, table, dataType, dataSize)); err != nil { t.Fatal(err) } } // use trancactions: // SQL Error 596 - LOB streaming is not permitted in auto-commit mode tx, err := db.Begin() if err != nil { t.Fatal(err) } stmt, err := tx.Prepare(fmt.Sprintf("insert into %s.%s values(?, ?)", TestSchema, table)) if err != nil { t.Fatal(err) } for i, in := range testData { switch in := in.(type) { case Lob: in.rd.(*bytes.Reader).Seek(0, io.SeekStart) case NullLob: in.Lob.rd.(*bytes.Reader).Seek(0, io.SeekStart) } if _, err := stmt.Exec(i, in); err != nil { t.Fatal(err) } } if err := tx.Commit(); err != nil { t.Fatal(err) } size := len(testData) var i int if err := db.QueryRow(fmt.Sprintf("select count(*) from %s.%s", TestSchema, table)).Scan(&i); err != nil { t.Fatal(err) } if i != size { t.Fatalf("rows %d - expected %d", i, size) } rows, err := db.Query(fmt.Sprintf("select * from %s.%s order by i", TestSchema, table)) if err != nil { t.Fatal(err) } defer rows.Close() var timestampCheck = equalLongdate if driverDataFormatVersion == 1 { timestampCheck = equalTimestamp } i = 0 for rows.Next() { in := testData[i] out := reflect.New(reflect.TypeOf(in)).Interface() switch out := out.(type) { case *NullDecimal: out.Decimal = (*Decimal)(new(big.Rat)) case *Lob: out.SetWriter(new(bytes.Buffer)) case *NullLob: out.Lob = new(Lob).SetWriter(new(bytes.Buffer)) } if err := rows.Scan(&i, out); err != nil { log.Fatal(err) } switch out := out.(type) { default: t.Fatalf("%d unknown type %T", i, out) case *uint8: if *out != in.(uint8) { t.Fatalf("%d value %v - expected %v", i, *out, in) } case *int16: if *out != in.(int16) { t.Fatalf("%d value %v - expected %v", i, *out, in) } case *int32: if *out != in.(int32) { t.Fatalf("%d value %v - expected %v", i, *out, in) } case *int64: if *out != in.(int64) { t.Fatalf("%d value %v - expected %v", i, *out, in) } case *float32: if *out != in.(float32) { t.Fatalf("%d value %v - expected %v", i, *out, in) } case *float64: if *out != in.(float64) { t.Fatalf("%d value %v - expected %v", i, *out, in) } case *string: if fixedSize { if !compareStringFixSize(in.(string), *out) { t.Fatalf("%d value %v - expected %v", i, *out, in) } } else { if *out != in.(string) { t.Fatalf("%d value %v - expected %v", i, *out, in) } } case *[]byte: if fixedSize { if !compareBytesFixSize(in.([]byte), *out) { t.Fatalf("%d value %v - expected %v", i, *out, in) } } else { if bytes.Compare(*out, in.([]byte)) != 0 { t.Fatalf("%d value %v - expected %v", i, *out, in) } } case *time.Time: switch dataType { default: t.Fatalf("unknown data type %s", dataType) case "date", "daydate": if !equalDate(*out, in.(time.Time)) { t.Fatalf("%d value %v - expected %v", i, *out, in) } case "time", "secondtime": if !equalTime(*out, in.(time.Time)) { t.Fatalf("%d value %v - expected %v", i, *out, in) } case "timestamp", "longdate": if !timestampCheck(*out, in.(time.Time)) { t.Fatalf("%d value %v - expected %v", i, *out, in) } case "seconddate": if !equalDateTime(*out, in.(time.Time)) { t.Fatalf("%d value %v - expected %v", i, *out, in) } } case **Decimal: if ((*big.Rat)(*out)).Cmp((*big.Rat)(in.(*Decimal))) != 0 { t.Fatalf("%d value %s - expected %s", i, (*big.Rat)(*out), (*big.Rat)(in.(*Decimal))) } case *bool: if *out != in.(bool) { t.Fatalf("%d value %v - expected %v", i, *out, in) } case *Lob: inLob := in.(Lob) ok, err := compareLob(&inLob, out) if err != nil { t.Fatal(err) } if !ok { t.Fatalf("%d lob content no equal", i) } case *sql.NullInt64: in := in.(sql.NullInt64) if in.Valid != out.Valid { t.Fatalf("%d value %v - expected %v", i, out, in) } if in.Valid && in.Int64 != out.Int64 { t.Fatalf("%d value %v - expected %v", i, out, in) } case *sql.NullFloat64: in := in.(sql.NullFloat64) if in.Valid != out.Valid { t.Fatalf("%d value %v - expected %v", i, out, in) } if in.Valid && in.Float64 != out.Float64 { t.Fatalf("%d value %v - expected %v", i, out, in) } case *sql.NullString: in := in.(sql.NullString) if in.Valid != out.Valid { t.Fatalf("%d value %v - expected %v", i, out, in) } if in.Valid { if fixedSize { if !compareStringFixSize(in.String, out.String) { t.Fatalf("%d value %v - expected %v", i, *out, in) } } else { if in.String != out.String { t.Fatalf("%d value %v - expected %v", i, out, in) } } } case *NullBytes: in := in.(NullBytes) if in.Valid != out.Valid { t.Fatalf("%d value %v - expected %v", i, out, in) } if in.Valid { if fixedSize { if !compareBytesFixSize(in.Bytes, out.Bytes) { t.Fatalf("%d value %v - expected %v", i, *out, in) } } else { if bytes.Compare(in.Bytes, out.Bytes) != 0 { t.Fatalf("%d value %v - expected %v", i, out, in) } } } case *NullTime: in := in.(NullTime) if in.Valid != out.Valid { t.Fatalf("%d value %v - expected %v", i, out, in) } if in.Valid { switch dataType { default: t.Fatalf("unknown data type %s", dataType) case "date", "daydate": if !equalDate(out.Time, in.Time) { t.Fatalf("%d value %v - expected %v", i, *out, in) } case "time", "secondtime": if !equalTime(out.Time, in.Time) { t.Fatalf("%d value %v - expected %v", i, *out, in) } case "timestamp", "longdate": if !timestampCheck(out.Time, in.Time) { t.Fatalf("%d value %v - expected %v", i, *out, in) } case "seconddate": if !equalDateTime(out.Time, in.Time) { t.Fatalf("%d value %v - expected %v", i, *out, in) } } } case *NullDecimal: in := in.(NullDecimal) if in.Valid != out.Valid { t.Fatalf("%d value %v - expected %v", i, out, in) } if in.Valid { if ((*big.Rat)(in.Decimal)).Cmp((*big.Rat)(out.Decimal)) != 0 { t.Fatalf("%d value %s - expected %s", i, (*big.Rat)(in.Decimal), (*big.Rat)(in.Decimal)) } } case *sql.NullBool: in := in.(sql.NullBool) if in.Valid != out.Valid { t.Fatalf("%d value %v - expected %v", i, out, in) } if in.Valid && in.Bool != out.Bool { t.Fatalf("%d value %v - expected %v", i, out, in) } case *NullLob: in := in.(NullLob) if in.Valid != out.Valid { t.Fatalf("%d value %v - expected %v", i, out, in) } if in.Valid { ok, err := compareLob(in.Lob, out.Lob) if err != nil { t.Fatal(err) } if !ok { t.Fatalf("%d lob content no equal", i) } } } i++ } if err := rows.Err(); err != nil { log.Fatal(err) } } // helper type testLobFile struct { content []byte isASCII bool } var testLobFiles []*testLobFile = make([]*testLobFile, 0) var testInitLobFilesOnce sync.Once func testInitLobFiles(t *testing.T) { testInitLobFilesOnce.Do(func() { filter := func(name string) bool { for _, ext := range []string{".go"} { if filepath.Ext(name) == ext { return true } } return false } walk := func(path string, info os.FileInfo, err error) error { if !info.IsDir() && filter(info.Name()) { content, err := ioutil.ReadFile(path) if err != nil { t.Fatal(err) } testLobFiles = append(testLobFiles, &testLobFile{isASCII: isASCII(content), content: content}) } return nil } root, err := os.Getwd() if err != nil { t.Fatal(err) } filepath.Walk(root, walk) }) } func isASCII(content []byte) bool { for _, b := range content { if b >= utf8.RuneSelf { return false } } return true } func compareLob(in, out *Lob) (bool, error) { in.rd.(*bytes.Reader).Seek(0, io.SeekStart) content, err := ioutil.ReadAll(in.rd) if err != nil { return false, err } if !bytes.Equal(content, out.wr.(*bytes.Buffer).Bytes()) { return false, nil } return true, nil } func compareStringFixSize(in, out string) bool { if in != out[:len(in)] { return false } for _, r := range out[len(in):] { if r != rune(' ') { return false } } return true } func compareBytesFixSize(in, out []byte) bool { if bytes.Compare(in, out[:len(in)]) != 0 { return false } for _, r := range out[len(in):] { if r != 0 { return false } } return true } func equalDate(t1, t2 time.Time) bool { u1 := t1.UTC() u2 := t2.UTC() return u1.Year() == u2.Year() && u1.Month() == u2.Month() && u1.Day() == u2.Day() } func equalTime(t1, t2 time.Time) bool { u1 := t1.UTC() u2 := t2.UTC() return u1.Hour() == u2.Hour() && u1.Minute() == u2.Minute() && u1.Second() == u2.Second() } func equalDateTime(t1, t2 time.Time) bool { return equalDate(t1, t2) && equalTime(t1, t2) } // equalMillisecond tests if the nanosecond part of two time types rounded to milliseconds are equal. func equalMilliSecond(t1, t2 time.Time) bool { u1 := t1.UTC() u2 := t2.UTC() return u1.Round(time.Millisecond).Nanosecond() == u2.Round(time.Millisecond).Nanosecond() } func equalTimestamp(t1, t2 time.Time) bool { return equalDate(t1, t2) && equalTime(t1, t2) && equalMilliSecond(t1, t2) } func equalLongdate(t1, t2 time.Time) bool { //HDB: nanosecond 7-digit precision return equalDate(t1, t2) && equalTime(t1, t2) && (t1.Nanosecond()/100) == (t2.Nanosecond()/100) } golang-github-sap-go-hdb-0.14.1/driver/decimal.go000066400000000000000000000166571346210761100215030ustar00rootroot00000000000000/* 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() } golang-github-sap-go-hdb-0.14.1/driver/decimal_test.go000066400000000000000000000121341346210761100225240ustar00rootroot00000000000000/* 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 ( "math/big" "testing" ) func TestDecimalInfo(t *testing.T) { t.Logf("maximum decimal value %v", maxDecimal) t.Logf("~log2(10): %f", lg10) } type testDigits10 struct { x *big.Int digits int } var testDigits10Data = []*testDigits10{ &testDigits10{new(big.Int).SetInt64(0), 1}, &testDigits10{new(big.Int).SetInt64(1), 1}, &testDigits10{new(big.Int).SetInt64(9), 1}, &testDigits10{new(big.Int).SetInt64(10), 2}, &testDigits10{new(big.Int).SetInt64(99), 2}, &testDigits10{new(big.Int).SetInt64(100), 3}, &testDigits10{new(big.Int).SetInt64(999), 3}, &testDigits10{new(big.Int).SetInt64(1000), 4}, &testDigits10{new(big.Int).SetInt64(9999), 4}, &testDigits10{new(big.Int).SetInt64(10000), 5}, &testDigits10{new(big.Int).SetInt64(99999), 5}, &testDigits10{new(big.Int).SetInt64(100000), 6}, &testDigits10{new(big.Int).SetInt64(999999), 6}, &testDigits10{new(big.Int).SetInt64(1000000), 7}, &testDigits10{new(big.Int).SetInt64(9999999), 7}, &testDigits10{new(big.Int).SetInt64(10000000), 8}, &testDigits10{new(big.Int).SetInt64(99999999), 8}, &testDigits10{new(big.Int).SetInt64(100000000), 9}, &testDigits10{new(big.Int).SetInt64(999999999), 9}, &testDigits10{new(big.Int).SetInt64(1000000000), 10}, &testDigits10{new(big.Int).SetInt64(9999999999), 10}, } func TestDigits10(t *testing.T) { for i, d := range testDigits10Data { digits := digits10(d.x) if d.digits != digits { t.Fatalf("value %d int %s digits %d - expected digits %d", i, d.x, digits, d.digits) } } } type testRat struct { // in x *big.Rat digits int minExp int maxExp int // out cmp *big.Int neg bool exp int df decFlags } var testRatData = []*testRat{ &testRat{new(big.Rat).SetFrac64(0, 1), 3, -2, 2, new(big.Int).SetInt64(0), false, 0, 0}, //convert 0 &testRat{new(big.Rat).SetFrac64(1, 1), 3, -2, 2, new(big.Int).SetInt64(1), false, 0, 0}, //convert 1 &testRat{new(big.Rat).SetFrac64(1, 10), 3, -2, 2, new(big.Int).SetInt64(1), false, -1, 0}, //convert 1/10 &testRat{new(big.Rat).SetFrac64(1, 99), 3, -2, 2, new(big.Int).SetInt64(1), false, -2, dfNotExact}, //convert 1/99 &testRat{new(big.Rat).SetFrac64(1, 100), 3, -2, 2, new(big.Int).SetInt64(1), false, -2, 0}, //convert 1/100 &testRat{new(big.Rat).SetFrac64(1, 1000), 3, -2, 2, new(big.Int).SetInt64(1), false, -3, dfUnderflow}, //convert 1/1000 &testRat{new(big.Rat).SetFrac64(10, 1), 3, -2, 2, new(big.Int).SetInt64(1), false, 1, 0}, //convert 10 &testRat{new(big.Rat).SetFrac64(100, 1), 3, -2, 2, new(big.Int).SetInt64(1), false, 2, 0}, //convert 100 &testRat{new(big.Rat).SetFrac64(1000, 1), 3, -2, 2, new(big.Int).SetInt64(10), false, 2, 0}, //convert 1000 &testRat{new(big.Rat).SetFrac64(10000, 1), 3, -2, 2, new(big.Int).SetInt64(100), false, 2, 0}, //convert 10000 &testRat{new(big.Rat).SetFrac64(100000, 1), 3, -2, 2, new(big.Int).SetInt64(100), false, 3, dfOverflow}, //convert 100000 &testRat{new(big.Rat).SetFrac64(999999, 1), 3, -2, 2, new(big.Int).SetInt64(100), false, 4, dfNotExact | dfOverflow}, //convert 999999 &testRat{new(big.Rat).SetFrac64(99999, 1), 3, -2, 2, new(big.Int).SetInt64(100), false, 3, dfNotExact | dfOverflow}, //convert 99999 &testRat{new(big.Rat).SetFrac64(9999, 1), 3, -2, 2, new(big.Int).SetInt64(100), false, 2, dfNotExact}, //convert 9999 &testRat{new(big.Rat).SetFrac64(99950, 1), 3, -2, 2, new(big.Int).SetInt64(100), false, 3, dfNotExact | dfOverflow}, //convert 99950 &testRat{new(big.Rat).SetFrac64(99949, 1), 3, -2, 2, new(big.Int).SetInt64(999), false, 2, dfNotExact}, //convert 99949 &testRat{new(big.Rat).SetFrac64(1, 3), 5, -5, 5, new(big.Int).SetInt64(33333), false, -5, dfNotExact}, //convert 1/3 &testRat{new(big.Rat).SetFrac64(2, 3), 5, -5, 5, new(big.Int).SetInt64(66667), false, -5, dfNotExact}, //convert 2/3 &testRat{new(big.Rat).SetFrac64(11, 2), 5, -5, 5, new(big.Int).SetInt64(55), false, -1, 0}, //convert 11/2 } func TestConvertRat(t *testing.T) { m := new(big.Int) for i := 0; i < 1; i++ { // use for performance tests for j, d := range testRatData { neg, exp, df := convertRatToDecimal(d.x, m, d.digits, d.minExp, d.maxExp) if m.Cmp(d.cmp) != 0 || neg != d.neg || exp != d.exp || df != d.df { t.Fatalf("converted %d value m %s neg %t exp %d df %b - expected m %s neg %t exp %d df %b", j, m, neg, exp, df, d.cmp, d.neg, d.exp, d.df) } } } } golang-github-sap-go-hdb-0.14.1/driver/decimalexample_test.go000066400000000000000000000034521346210761100241030ustar00rootroot00000000000000/* 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" "fmt" "log" "math/big" ) /* ExampleDecimal creates a table with a single decimal attribute, insert a record into it and select the entry afterwards. This demonstrates the usage of the type Decimal to write and scan decimal database attributes. For variables TestDSN and TestSchema see main_test.go. */ func ExampleDecimal() { db, err := sql.Open(DriverName, TestDSN) if err != nil { log.Fatal(err) } defer db.Close() tableName := RandomIdentifier("table_") if _, err := db.Exec(fmt.Sprintf("create table %s.%s (x decimal)", TestSchema, tableName)); err != nil { // Create table with decimal attribute. log.Fatal(err) } // Decimal values are represented in Go as big.Rat. in := (*Decimal)(big.NewRat(1, 1)) // Create *big.Rat and cast to Decimal. if _, err := db.Exec(fmt.Sprintf("insert into %s.%s values(?)", TestSchema, tableName), in); err != nil { // Insert record. log.Fatal(err) } var out Decimal // Declare scan variable. if err := db.QueryRow(fmt.Sprintf("select * from %s.%s", TestSchema, tableName)).Scan(&out); err != nil { log.Fatal(err) } fmt.Printf("Decimal value: %s", (*big.Rat)(&out).String()) // Cast scan variable to *big.Rat to use *big.Rat methods. // output: Decimal value: 1/1 } golang-github-sap-go-hdb-0.14.1/driver/doc.go000066400000000000000000000012271346210761100206350ustar00rootroot00000000000000/* 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 golang-github-sap-go-hdb-0.14.1/driver/driver.go000066400000000000000000000362341346210761100213710ustar00rootroot00000000000000/* 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 ( "context" "database/sql" "database/sql/driver" "errors" "fmt" "io" "reflect" "sync" "time" "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.14.1" // DriverName is the driver name to use with sql.Open for hdb databases. const DriverName = "hdb" // Transaction isolation levels supported by hdb. const ( LevelReadCommitted = "READ COMMITTED" LevelRepeatableRead = "REPEATABLE READ" LevelSerializable = "SERIALIZABLE" ) // Access modes supported by hdb. const ( modeReadOnly = "READ ONLY" modeReadWrite = "READ WRITE" ) // map sql isolation level to hdb isolation level. var isolationLevel = map[driver.IsolationLevel]string{ driver.IsolationLevel(sql.LevelDefault): LevelReadCommitted, driver.IsolationLevel(sql.LevelReadCommitted): LevelReadCommitted, driver.IsolationLevel(sql.LevelRepeatableRead): LevelRepeatableRead, driver.IsolationLevel(sql.LevelSerializable): LevelSerializable, } // map sql read only flag to hdb access mode. var readOnly = map[bool]string{ true: modeReadOnly, false: modeReadWrite, } // ErrUnsupportedIsolationLevel is the error raised if a transaction is started with a not supported isolation level. var ErrUnsupportedIsolationLevel = errors.New("Unsupported isolation level") // ErrNestedTransaction is the error raised if a tranasction is created within a transaction as this is not supported by hdb. var ErrNestedTransaction = errors.New("Nested transactions are not supported") // needed for testing const driverDataFormatVersion = 1 // queries const ( pingQuery = "select 1 from dummy" isolationLevelStmt = "set transaction isolation level %s" accessModeStmt = "set transaction %s" sessionVariable = "set %s=%s" ) // bulk statement const ( bulk = "b$" ) var ( flushTok = new(struct{}) noFlushTok = new(struct{}) ) var ( // NoFlush is to be used as parameter in bulk statements to delay execution. NoFlush = sql.Named(bulk, &noFlushTok) // Flush can be used as optional parameter in bulk statements but is not required to trigger execution. Flush = sql.Named(bulk, &flushTok) ) var drv = &hdbDrv{} func init() { sql.Register(DriverName, drv) } // driver // check if driver implements all required interfaces var ( _ driver.Driver = (*hdbDrv)(nil) _ driver.DriverContext = (*hdbDrv)(nil) ) type hdbDrv struct{} func (d *hdbDrv) Open(dsn string) (driver.Conn, error) { connector, err := NewDSNConnector(dsn) if err != nil { return nil, err } return connector.Connect(context.Background()) } func (d *hdbDrv) OpenConnector(dsn string) (driver.Connector, error) { return NewDSNConnector(dsn) } // database connection // check if conn implements all required interfaces var ( _ driver.Conn = (*conn)(nil) _ driver.ConnPrepareContext = (*conn)(nil) _ driver.Pinger = (*conn)(nil) _ driver.ConnBeginTx = (*conn)(nil) _ driver.ExecerContext = (*conn)(nil) //go 1.9 issue (ExecerContext is only called if Execer is implemented) _ driver.Execer = (*conn)(nil) _ driver.QueryerContext = (*conn)(nil) //go 1.9 issue (QueryerContext is only called if Queryer is implemented) // QueryContext is needed for stored procedures with table output parameters. _ driver.Queryer = (*conn)(nil) _ driver.NamedValueChecker = (*conn)(nil) ) type conn struct { session *p.Session } func newConn(ctx context.Context, c *Connector) (driver.Conn, error) { session, err := p.NewSession(ctx, c) if err != nil { return nil, err } conn := &conn{session: session} if err := conn.init(ctx, c.sessionVariables); err != nil { return nil, err } return conn, nil } func (c *conn) init(ctx context.Context, sv SessionVariables) error { if sv == nil { return nil } for k, v := range sv { if _, err := c.ExecContext(ctx, fmt.Sprintf(sessionVariable, fmt.Sprintf("'%s'", k), fmt.Sprintf("'%s'", v)), nil); err != nil { return err } } return nil } func (c *conn) Prepare(query string) (driver.Stmt, error) { panic("deprecated") } func (c *conn) Close() error { c.session.Close() return nil } func (c *conn) Begin() (driver.Tx, error) { panic("deprecated") } func (c *conn) BeginTx(ctx context.Context, opts driver.TxOptions) (tx driver.Tx, err error) { if c.session.IsBad() { return nil, driver.ErrBadConn } if c.session.InTx() { return nil, ErrNestedTransaction } level, ok := isolationLevel[opts.Isolation] if !ok { return nil, ErrUnsupportedIsolationLevel } done := make(chan struct{}) go func() { // set isolation level if _, err = c.ExecContext(ctx, fmt.Sprintf(isolationLevelStmt, level), nil); err != nil { goto done } // set access mode if _, err = c.ExecContext(ctx, fmt.Sprintf(accessModeStmt, readOnly[opts.ReadOnly]), nil); err != nil { goto done } c.session.SetInTx(true) tx = newTx(c.session) done: close(done) }() select { case <-ctx.Done(): return nil, ctx.Err() case <-done: return tx, err } } // Exec implements the database/sql/driver/Execer interface. // delete after go 1.9 compatibility is given up. func (c *conn) Exec(query string, args []driver.Value) (driver.Result, error) { panic("deprecated") } // ExecContext implements the database/sql/driver/ExecerContext interface. func (c *conn) ExecContext(ctx context.Context, query string, args []driver.NamedValue) (r driver.Result, err 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) done := make(chan struct{}) go func() { r, err = c.session.ExecDirect(query) close(done) }() select { case <-ctx.Done(): return nil, ctx.Err() case <-done: return r, err } } // Queryer implements the database/sql/driver/Queryer interface. // delete after go 1.9 compatibility is given up. func (c *conn) Query(query string, args []driver.Value) (driver.Rows, error) { panic("deprecated") } func (c *conn) Ping(ctx context.Context) (err error) { if c.session.IsBad() { return driver.ErrBadConn } done := make(chan struct{}) go func() { _, err = c.QueryContext(ctx, pingQuery, nil) close(done) }() select { case <-ctx.Done(): return ctx.Err() case <-done: return err } } // CheckNamedValue implements NamedValueChecker interface. // implemented for conn: // if querier or execer is called, sql checks parameters before // in case of parameters the method can be 'skipped' and force the prepare path // --> guarantee that a valid driver value is returned // --> if not implemented, Lob need to have a pseudo Value method to return a valid driver value func (c *conn) CheckNamedValue(nv *driver.NamedValue) error { switch nv.Value.(type) { case Lob, *Lob: nv.Value = nil } return nil } //transaction // check if tx implements all required interfaces var ( _ driver.Tx = (*tx)(nil) ) 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 var argsPool = sync.Pool{} // check if stmt implements all required interfaces var ( _ driver.Stmt = (*stmt)(nil) _ driver.StmtExecContext = (*stmt)(nil) _ driver.StmtQueryContext = (*stmt)(nil) _ driver.NamedValueChecker = (*stmt)(nil) ) type stmt struct { qt p.QueryType session *p.Session query string id uint64 prmFieldSet *p.ParameterFieldSet resultFieldSet *p.ResultFieldSet bulk, noFlush bool numArg int args []driver.NamedValue } func newStmt(qt p.QueryType, session *p.Session, query string, id uint64, prmFieldSet *p.ParameterFieldSet, resultFieldSet *p.ResultFieldSet) (*stmt, error) { return &stmt{qt: qt, session: session, query: query, id: id, prmFieldSet: prmFieldSet, resultFieldSet: resultFieldSet}, nil } func (s *stmt) Close() error { if s.args != nil { if len(s.args) != 0 { sqltrace.Tracef("close: %s - not flushed records: %d)", s.query, int(len(s.args)/s.NumInput())) } argsPool.Put(s.args) s.args = nil } 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) { panic("deprecated") } func (s *stmt) ExecContext(ctx context.Context, args []driver.NamedValue) (r driver.Result, err 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) // init noFlush noFlush := s.noFlush s.noFlush = false var _args []driver.NamedValue done := make(chan struct{}) if !s.bulk { go func() { r, err = s.session.Exec(s.id, s.prmFieldSet, args) close(done) }() goto done } if s.args == nil { s.args, _ = argsPool.Get().([]driver.NamedValue) if s.args == nil { s.args = make([]driver.NamedValue, 0, len(args)*1000) } s.args = s.args[:0] } s.args = append(s.args, args...) s.numArg++ if noFlush && s.numArg < maxSmallint { //TODO: check why bigArgument count does not work return driver.ResultNoRows, nil } _args, _ = argsPool.Get().([]driver.NamedValue) if _args == nil || cap(_args) < len(s.args) { _args = make([]driver.NamedValue, len(s.args)) } _args = _args[:len(s.args)] copy(_args, s.args) s.args = s.args[:0] s.numArg = 0 go func() { r, err = s.session.Exec(s.id, s.prmFieldSet, _args) argsPool.Put(_args) close(done) }() done: select { case <-ctx.Done(): return nil, ctx.Err() case <-done: return r, err } } func (s *stmt) Query(args []driver.Value) (rows driver.Rows, err error) { panic("deprecated") } // Deprecated: see NamedValueChecker. //func (s *stmt) ColumnConverter(idx int) driver.ValueConverter { //} // CheckNamedValue implements NamedValueChecker interface. func (s *stmt) CheckNamedValue(nv *driver.NamedValue) error { if nv.Name == bulk { if ptr, ok := nv.Value.(**struct{}); ok { switch ptr { case &noFlushTok: s.bulk, s.noFlush = true, true return driver.ErrRemoveArgument case &flushTok: return driver.ErrRemoveArgument } } } return checkNamedValue(s.prmFieldSet, nv) } // 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) // check if noResultType implements all required interfaces var ( _ driver.Rows = (*noResultType)(nil) ) 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 } // rows type rows struct { } // query result // check if queryResult implements all required interfaces var ( _ driver.Rows = (*queryResult)(nil) _ driver.RowsColumnTypeDatabaseTypeName = (*queryResult)(nil) // go 1.8 _ driver.RowsColumnTypeLength = (*queryResult)(nil) // go 1.8 _ driver.RowsColumnTypeNullable = (*queryResult)(nil) // go 1.8 _ driver.RowsColumnTypePrecisionScale = (*queryResult)(nil) // go 1.8 _ driver.RowsColumnTypeScanType = (*queryResult)(nil) // go 1.8 ) type queryResult struct { session *p.Session id uint64 resultFieldSet *p.ResultFieldSet fieldValues *p.FieldValues pos int attrs p.PartAttributes columns []string lastErr error } func newQueryResult(session *p.Session, id uint64, resultFieldSet *p.ResultFieldSet, fieldValues *p.FieldValues, attrs p.PartAttributes) (driver.Rows, error) { columns := make([]string, resultFieldSet.NumField()) for i := 0; i < len(columns); i++ { columns[i] = resultFieldSet.Field(i).Name() } return &queryResult{ session: session, id: id, resultFieldSet: resultFieldSet, 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.attrs, err = r.session.FetchNext(r.id, r.resultFieldSet, r.fieldValues); 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 } func (r *queryResult) ColumnTypeDatabaseTypeName(idx int) string { return r.resultFieldSet.Field(idx).TypeCode().TypeName() } func (r *queryResult) ColumnTypeLength(idx int) (int64, bool) { return r.resultFieldSet.Field(idx).TypeLength() } func (r *queryResult) ColumnTypePrecisionScale(idx int) (int64, int64, bool) { return r.resultFieldSet.Field(idx).TypePrecisionScale() } func (r *queryResult) ColumnTypeNullable(idx int) (bool, bool) { return r.resultFieldSet.Field(idx).Nullable(), true } var ( scanTypeUnknown = reflect.TypeOf(new(interface{})).Elem() scanTypeTinyint = reflect.TypeOf(uint8(0)) scanTypeSmallint = reflect.TypeOf(int16(0)) scanTypeInteger = reflect.TypeOf(int32(0)) scanTypeBigint = reflect.TypeOf(int64(0)) scanTypeReal = reflect.TypeOf(float32(0.0)) scanTypeDouble = reflect.TypeOf(float64(0.0)) scanTypeTime = reflect.TypeOf(time.Time{}) scanTypeString = reflect.TypeOf(string("")) scanTypeBytes = reflect.TypeOf([]byte{}) scanTypeDecimal = reflect.TypeOf(Decimal{}) scanTypeLob = reflect.TypeOf(Lob{}) ) func (r *queryResult) ColumnTypeScanType(idx int) reflect.Type { switch r.resultFieldSet.Field(idx).TypeCode().DataType() { default: return scanTypeUnknown case p.DtTinyint: return scanTypeTinyint case p.DtSmallint: return scanTypeSmallint case p.DtInteger: return scanTypeInteger case p.DtBigint: return scanTypeBigint case p.DtReal: return scanTypeReal case p.DtDouble: return scanTypeDouble case p.DtTime: return scanTypeTime case p.DtDecimal: return scanTypeDecimal case p.DtString: return scanTypeString case p.DtBytes: return scanTypeBytes case p.DtLob: return scanTypeLob } } golang-github-sap-go-hdb-0.14.1/driver/driver_future.go000066400000000000000000000067651346210761100227710ustar00rootroot00000000000000// +build future /* Copyright 2018 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 ( "context" //"database/sql" "database/sql/driver" "github.com/SAP/go-hdb/driver/sqltrace" p "github.com/SAP/go-hdb/internal/protocol" ) // database connection func (c *conn) PrepareContext(ctx context.Context, query string) (stmt driver.Stmt, err error) { if c.session.IsBad() { return nil, driver.ErrBadConn } done := make(chan struct{}) go func() { var ( qt p.QueryType id uint64 prmFieldSet *p.ParameterFieldSet resultFieldSet *p.ResultFieldSet ) qt, id, prmFieldSet, resultFieldSet, err = c.session.Prepare(query) if err != nil { goto done } select { default: case <-ctx.Done(): return } stmt, err = newStmt(qt, c.session, query, id, prmFieldSet, resultFieldSet) done: close(done) }() select { case <-ctx.Done(): return nil, ctx.Err() case <-done: return stmt, err } } // QueryContext implements the database/sql/driver/QueryerContext interface. func (c *conn) QueryContext(ctx context.Context, query string, args []driver.NamedValue) (rows driver.Rows, err 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) done := make(chan struct{}) go func() { var ( id uint64 resultFieldSet *p.ResultFieldSet fieldValues *p.FieldValues attributes p.PartAttributes ) id, resultFieldSet, fieldValues, attributes, err = c.session.QueryDirect(query) if err != nil { goto done } select { default: case <-ctx.Done(): return } if id == 0 { // non select query rows = noResult } else { rows, err = newQueryResult(c.session, id, resultFieldSet, fieldValues, attributes) } done: close(done) }() select { case <-ctx.Done(): return nil, ctx.Err() case <-done: return rows, err } } //statement func (s *stmt) QueryContext(ctx context.Context, args []driver.NamedValue) (rows driver.Rows, err error) { if s.session.IsBad() { return nil, driver.ErrBadConn } done := make(chan struct{}) go func() { rows, err = s.defaultQuery(ctx, args) close(done) }() select { case <-ctx.Done(): return nil, ctx.Err() case <-done: return rows, err } } func (s *stmt) defaultQuery(ctx context.Context, args []driver.NamedValue) (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 } select { default: case <-ctx.Done(): return nil, ctx.Err() } if rid == 0 { // non select query return noResult, nil } return newQueryResult(s.session, rid, s.resultFieldSet, values, attributes) } golang-github-sap-go-hdb-0.14.1/driver/driver_legacy.go000066400000000000000000000263411346210761100227130ustar00rootroot00000000000000// +build !future /* Copyright 2018 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 ( "context" "database/sql/driver" "encoding/binary" "fmt" "io" "regexp" "sync" "github.com/SAP/go-hdb/driver/sqltrace" p "github.com/SAP/go-hdb/internal/protocol" ) 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) } // database connection func (c *conn) PrepareContext(ctx context.Context, query string) (stmt driver.Stmt, err error) { if c.session.IsBad() { return nil, driver.ErrBadConn } done := make(chan struct{}) go func() { prepareQuery, bulkInsert := checkBulkInsert(query) var ( qt p.QueryType id uint64 prmFieldSet *p.ParameterFieldSet resultFieldSet *p.ResultFieldSet ) qt, id, prmFieldSet, resultFieldSet, err = c.session.Prepare(prepareQuery) if err != nil { goto done } select { default: case <-ctx.Done(): return } if bulkInsert { stmt, err = newBulkInsertStmt(c.session, prepareQuery, id, prmFieldSet) } else { stmt, err = newStmt(qt, c.session, prepareQuery, id, prmFieldSet, resultFieldSet) } done: close(done) }() select { case <-ctx.Done(): return nil, ctx.Err() case <-done: return stmt, err } } // QueryContext implements the database/sql/driver/QueryerContext interface. func (c *conn) QueryContext(ctx context.Context, query string, args []driver.NamedValue) (rows driver.Rows, err 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)) } done := make(chan struct{}) go func() { var ( id uint64 resultFieldSet *p.ResultFieldSet fieldValues *p.FieldValues attributes p.PartAttributes ) id, resultFieldSet, fieldValues, attributes, err = c.session.QueryDirect(query) if err != nil { goto done } select { default: case <-ctx.Done(): return } if id == 0 { // non select query rows = noResult } else { rows, err = newQueryResult(c.session, id, resultFieldSet, fieldValues, attributes) } done: close(done) }() select { case <-ctx.Done(): return nil, ctx.Err() case <-done: return rows, err } } //statement func (s *stmt) QueryContext(ctx context.Context, args []driver.NamedValue) (rows driver.Rows, err error) { if s.session.IsBad() { return nil, driver.ErrBadConn } done := make(chan struct{}) go func() { switch s.qt { default: rows, err = s.defaultQuery(ctx, args) case p.QtProcedureCall: rows, err = s.procedureCall(ctx, args) } close(done) }() select { case <-ctx.Done(): return nil, ctx.Err() case <-done: return rows, err } } func (s *stmt) defaultQuery(ctx context.Context, args []driver.NamedValue) (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 } select { default: case <-ctx.Done(): return nil, ctx.Err() } if rid == 0 { // non select query return noResult, nil } return newQueryResult(s.session, rid, s.resultFieldSet, values, attributes) } func (s *stmt) procedureCall(ctx context.Context, args []driver.NamedValue) (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 } select { default: case <-ctx.Done(): return nil, ctx.Err() } return newProcedureCallResult(s.session, s.prmFieldSet, fieldValues, tableResults) } // bulk insert statement // check if bulkInsertStmt implements all required interfaces var ( _ driver.Stmt = (*bulkInsertStmt)(nil) _ driver.StmtExecContext = (*bulkInsertStmt)(nil) _ driver.StmtQueryContext = (*bulkInsertStmt)(nil) _ driver.NamedValueChecker = (*bulkInsertStmt)(nil) ) type bulkInsertStmt struct { session *p.Session query string id uint64 prmFieldSet *p.ParameterFieldSet numArg int args []driver.NamedValue } func newBulkInsertStmt(session *p.Session, query string, id uint64, prmFieldSet *p.ParameterFieldSet) (*bulkInsertStmt, error) { return &bulkInsertStmt{session: session, query: query, id: id, prmFieldSet: prmFieldSet, args: make([]driver.NamedValue, 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) { panic("deprecated") } func (s *bulkInsertStmt) ExecContext(ctx context.Context, args []driver.NamedValue) (r driver.Result, err error) { if s.session.IsBad() { return nil, driver.ErrBadConn } sqltrace.Tracef("%s %v", s.query, args) done := make(chan struct{}) go func() { if args == nil || len(args) == 0 { r, err = s.execFlush() } else { r, err = s.execBuffer(args) } close(done) }() select { case <-ctx.Done(): return nil, ctx.Err() case <-done: return r, err } } func (s *bulkInsertStmt) execFlush() (driver.Result, error) { if s.numArg == 0 { return driver.ResultNoRows, nil } sqltrace.Traceln("execFlush") result, err := s.session.Exec(s.id, s.prmFieldSet, s.args) s.args = s.args[:0] s.numArg = 0 return result, err } func (s *bulkInsertStmt) execBuffer(args []driver.NamedValue) (driver.Result, error) { numField := s.prmFieldSet.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) { panic("deprecated") } func (s *bulkInsertStmt) QueryContext(ctx context.Context, args []driver.NamedValue) (driver.Rows, error) { return nil, fmt.Errorf("query not allowed in context of bulk insert statement %s", s.query) } // Deprecated: see NamedValueChecker. //func (s *bulkInsertStmt) ColumnConverter(idx int) driver.ValueConverter { //} // CheckNamedValue implements NamedValueChecker interface. func (s *bulkInsertStmt) CheckNamedValue(nv *driver.NamedValue) error { return checkNamedValue(s.prmFieldSet, nv) } //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 // check if procedureCallResult implements all required interfaces var _ driver.Rows = (*procedureCallResult)(nil) type procedureCallResult struct { id uint64 session *p.Session prmFieldSet *p.ParameterFieldSet fieldValues *p.FieldValues _tableRows []driver.Rows columns []string eof error } func newProcedureCallResult(session *p.Session, prmFieldSet *p.ParameterFieldSet, fieldValues *p.FieldValues, tableResults []*p.TableResult) (driver.Rows, error) { fieldIdx := prmFieldSet.NumOutputField() columns := make([]string, fieldIdx+len(tableResults)) for i := 0; i < fieldIdx; i++ { columns[i] = prmFieldSet.OutputField(i).Name() } 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, prmFieldSet: prmFieldSet, 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.prmFieldSet.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 } golang-github-sap-go-hdb-0.14.1/driver/driver_test.go000066400000000000000000000134661346210761100224320ustar00rootroot00000000000000/* 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 ( "context" "database/sql" "fmt" "strings" "testing" ) func TestPing(t *testing.T) { db, err := sql.Open(DriverName, TestDSN) if err != nil { t.Fatal(err) } defer db.Close() if err := db.Ping(); err != nil { t.Fatal(err) } if err := db.PingContext(context.Background()); err != nil { t.Fatal(err) } } func TestInsertByQuery(t *testing.T) { db, err := sql.Open(DriverName, TestDSN) if err != nil { t.Fatal(err) } defer db.Close() table := RandomIdentifier("insertByQuery_") if _, err := db.Exec(fmt.Sprintf("create table %s.%s (i integer)", TestSchema, table)); err != nil { t.Fatal(err) } // insert value via Query if err := db.QueryRow(fmt.Sprintf("insert into %s.%s values (?)", TestSchema, table), 42).Scan(); err != sql.ErrNoRows { t.Fatal(err) } // check value var i int if err := db.QueryRow(fmt.Sprintf("select * from %s.%s", TestSchema, table)).Scan(&i); err != nil { t.Fatal(err) } if i != 42 { t.Fatalf("value %d - expected %d", i, 42) } } func TestHDBError(t *testing.T) { db, err := sql.Open(DriverName, TestDSN) if err != nil { t.Fatal(err) } defer db.Close() //select from not existing table with different table name length //to check if padding, etc works (see hint in protocol.error.Read(...)) for i := 0; i < 9; i++ { _, err := db.Query(fmt.Sprintf("select * from %s.%s", TestSchema, strings.Repeat("x", i+1))) if err == nil { t.Fatal("hdb error expected") } dbError, ok := err.(Error) if !ok { t.Fatalf("hdb error expected got %v", err) } if dbError.Code() != 259 { t.Fatalf("hdb error code: %d - expected: %d", dbError.Code(), 259) } } } func TestHDBWarning(t *testing.T) { // procedure gives warning: // SQL HdbWarning 1347 - Not recommended feature: DDL statement is used in Dynamic SQL (current dynamic_sql_ddl_error_level = 1) const procOut = `create procedure %[1]s.%[2]s () language SQLSCRIPT as begin exec 'create table %[3]s(id int)'; exec 'drop table %[3]s'; end ` db, err := sql.Open(DriverName, TestDSN) if err != nil { t.Fatal(err) } defer db.Close() procedure := RandomIdentifier("proc_") tableName := RandomIdentifier("table_") if _, err := db.Exec(fmt.Sprintf(procOut, TestSchema, procedure, tableName)); err != nil { // Create stored procedure. t.Fatal(err) } if _, err := db.Exec(fmt.Sprintf("call %s.%s", TestSchema, procedure)); err != nil { t.Fatal(err) } } func TestQueryAttributeAlias(t *testing.T) { db, err := sql.Open(DriverName, TestDSN) if err != nil { t.Fatal(err) } defer db.Close() table := RandomIdentifier("queryAttributeAlias_") if _, err := db.Exec(fmt.Sprintf("create table %s.%s (i integer, j integer)", TestSchema, table)); err != nil { t.Fatal(err) } rows, err := db.Query(fmt.Sprintf("select i as x, j from %s.%s", TestSchema, table)) if err != nil { t.Fatal(err) } defer rows.Close() columns, err := rows.Columns() if err != nil { t.Fatal(err) } if columns[0] != "X" { t.Fatalf("value %s - expected %s", columns[0], "X") } if columns[1] != "J" { t.Fatalf("value %s - expected %s", columns[1], "J") } } func TestRowsAffected(t *testing.T) { const maxRows = 10 db, err := sql.Open(DriverName, TestDSN) if err != nil { t.Fatal(err) } defer db.Close() table := RandomIdentifier("rowsAffected_") if _, err := db.Exec(fmt.Sprintf("create table %s.%s (i integer)", TestSchema, table)); err != nil { t.Fatal(err) } stmt, err := db.Prepare(fmt.Sprintf("insert into %s.%s values(?)", TestSchema, table)) if err != nil { t.Fatal(err) } // insert for i := 0; i < maxRows; i++ { result, err := stmt.Exec(i) if err != nil { t.Fatal(err) } checkAffectedRows(t, result, 1) } // update result, err := db.Exec(fmt.Sprintf("update %s.%s set i = %d where i <> %d", TestSchema, table, maxRows, maxRows)) if err != nil { t.Fatal(err) } checkAffectedRows(t, result, maxRows) } func TestUpsert(t *testing.T) { db, err := sql.Open(DriverName, TestDSN) if err != nil { t.Fatal(err) } defer db.Close() table := RandomIdentifier("upsert_") if _, err := db.Exec(fmt.Sprintf("create table %s.%s (key int primary key, val int)", TestSchema, table)); err != nil { t.Fatal(err) } result, err := db.Exec(fmt.Sprintf("upsert %s.%s values (1, 1)", TestSchema, table)) if err != nil { t.Fatal(err) } checkAffectedRows(t, result, 1) result, err = db.Exec(fmt.Sprintf("upsert %s.%s values (:1, :1) where key = :2", TestSchema, table), 2, 2) if err != nil { t.Fatal(err) } checkAffectedRows(t, result, 1) result, err = db.Exec(fmt.Sprintf("upsert %s.%s values (?, ?) where key = ?", TestSchema, table), 1, 9, 1) if err != nil { t.Fatal(err) } checkAffectedRows(t, result, 1) result, err = db.Exec(fmt.Sprintf("upsert %s.%s values (?, ?) with primary key", TestSchema, table), 1, 8) if err != nil { t.Fatal(err) } checkAffectedRows(t, result, 1) result, err = db.Exec(fmt.Sprintf("upsert %[1]s.%[2]s select key + ?, val from %[1]s.%[2]s", TestSchema, table), 2) if err != nil { t.Fatal(err) } checkAffectedRows(t, result, 2) } func checkAffectedRows(t *testing.T, result sql.Result, rowsExpected int64) { rowsAffected, err := result.RowsAffected() if err != nil { t.Fatal(err) } if rowsAffected != rowsExpected { t.Fatalf("rows affected %d - expected %d", rowsAffected, rowsExpected) } } golang-github-sap-go-hdb-0.14.1/driver/dsn.go000066400000000000000000000045131346210761100206550ustar00rootroot00000000000000/* 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 // DSN 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. DSNFetchSize = "fetchSize" // Maximum number of fetched records from database by database/sql/driver/Rows.Next(). ) /* DSN TLS parameters. For more information please see https://golang.org/pkg/crypto/tls/#Config. For more flexibility in TLS configuration please see driver.Connector. */ const ( DSNTLSRootCAFile = "TLSRootCAFile" // Path,- filename to root certificate(s). DSNTLSServerName = "TLSServerName" // ServerName to verify the hostname. DSNTLSInsecureSkipVerify = "TLSInsecureSkipVerify" // Controls whether a client verifies the server's certificate chain and host name. ) // DSN default values. const ( DefaultTimeout = 300 // Default value connection timeout (300 seconds = 5 minutes). DefaultFetchSize = 128 // Default value fetchSize. ) // DSN minimal values. const ( minTimeout = 0 // Minimal timeout value. minFetchSize = 1 // Minimal fetchSize value. ) /* 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" Examples TLS connection: "hdb://myuser:mypassword@localhost:39013?TLSRootCAFile=trust.pem" "hdb://myuser:mypassword@localhost:39013?TLSRootCAFile=trust.pem&TLSServerName=hostname" "hdb://myuser:mypassword@localhost:39013?TLSInsecureSkipVerify" */ type DSN string golang-github-sap-go-hdb-0.14.1/driver/dsnexample_test.go000066400000000000000000000021631346210761100232670ustar00rootroot00000000000000/* 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_test import ( "database/sql" "log" "net/url" "github.com/SAP/go-hdb/driver" ) // dsn creates data source name with the help of the net/url package. func dsn() string { dsn := &url.URL{ Scheme: driver.DriverName, User: url.UserPassword("user", "password"), Host: "host:port", } return dsn.String() } // ExampleDSN shows how to construct a DSN (data source name) as url. func ExampleDSN() { db, err := sql.Open(driver.DriverName, dsn()) if err != nil { log.Fatal(err) } defer db.Close() if err := db.Ping(); err != nil { log.Fatal(err) } } golang-github-sap-go-hdb-0.14.1/driver/error.go000066400000000000000000000031741346210761100212240ustar00rootroot00000000000000/* 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 // HDB error levels. const ( HdbWarning = 0 HdbError = 1 HdbFatalError = 2 ) // Error represents errors send by the database server. type Error interface { Error() string // Implements the golang error interface. NumError() int // NumError returns the number of errors. SetIdx(idx int) // Sets the error index in case number of errors are greater 1 in the range of 0 <= index < NumError(). StmtNo() int // Returns the statement number of the error in multi statement contexts (e.g. bulk insert). 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() int // 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. } golang-github-sap-go-hdb-0.14.1/driver/errorexample_test.go000066400000000000000000000020101346210761100236230ustar00rootroot00000000000000/* 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_test import ( "database/sql" "log" "github.com/SAP/go-hdb/driver" ) func ExampleError() { db, err := sql.Open("hdb", "hdb://user:password@host:port") if err != nil { log.Fatal(err) } defer db.Close() stmt, err := db.Query("select * from dummy") if err != nil { // Type assertion of database server error message. if dbError, ok := err.(driver.Error); ok { log.Printf("code %d text %s", dbError.Code(), dbError.Text()) } } stmt.Close() } golang-github-sap-go-hdb-0.14.1/driver/example_test.go000066400000000000000000000016261346210761100225650ustar00rootroot00000000000000/* 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_test import ( "database/sql" "log" // Register hdb driver. _ "github.com/SAP/go-hdb/driver" ) const ( driverName = "hdb" hdbDsn = "hdb://user:password@host:port" ) func Example() { db, err := sql.Open(driverName, hdbDsn) if err != nil { log.Fatal(err) } defer db.Close() if err := db.Ping(); err != nil { log.Fatal(err) } } golang-github-sap-go-hdb-0.14.1/driver/identifier.go000066400000000000000000000025151346210761100222130ustar00rootroot00000000000000/* 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 // RandomIdentifier 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("%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) } golang-github-sap-go-hdb-0.14.1/driver/identifier_test.go000066400000000000000000000023351346210761100232520ustar00rootroot00000000000000/* 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 ( "testing" ) type testIdentifier struct { id Identifier s string } var testIdentifierData = []*testIdentifier{ &testIdentifier{"_", "_"}, &testIdentifier{"_A", "_A"}, &testIdentifier{"A#$_", "A#$_"}, &testIdentifier{"1", `"1"`}, &testIdentifier{"a", `"a"`}, &testIdentifier{"$", `"$"`}, &testIdentifier{"日本語", `"日本語"`}, &testIdentifier{"testTransaction", `"testTransaction"`}, &testIdentifier{"a.b.c", `"a.b.c"`}, &testIdentifier{"AAA.BBB.CCC", `"AAA.BBB.CCC"`}, } func TestIdentifierStringer(t *testing.T) { for i, d := range testIdentifierData { if d.id.String() != d.s { t.Fatalf("%d id %s - expected %s", i, d.id, d.s) } } } golang-github-sap-go-hdb-0.14.1/driver/lob.go000066400000000000000000000050301346210761100206400ustar00rootroot00000000000000/* 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 ( "fmt" "io" ) // 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 } // 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 // and return *Lob, to enable simple call chaining. func (l *Lob) SetReader(rd io.Reader) *Lob { l.rd = rd return l } // SetWriter sets the io.Writer destination for a lob field to be read from database // and return *Lob, to enable simple call chaining. func (l *Lob) SetWriter(wr io.Writer) *Lob { l.wr = wr return l } type writerSetter interface { SetWriter(w io.Writer) error } // Scan implements the database/sql/Scanner interface. func (l *Lob) Scan(src interface{}) error { if l.wr == nil { return fmt.Errorf("lob error: initial reader %[1]T %[1]v", l) } ws, ok := src.(writerSetter) if !ok { return fmt.Errorf("lob: invalid scan type %T", src) } if err := ws.SetWriter(l.wr); err != nil { return err } return nil } // NullLob represents an Lob that may be null. // NullLob implements the Scanner interface so // it can be used as a scan destination, similar to NullString. type NullLob struct { Lob *Lob Valid bool // Valid is true if Lob is not NULL } // Scan implements the database/sql/Scanner interface. func (l *NullLob) Scan(src interface{}) error { if src == nil { l.Valid = false return nil } if err := l.Lob.Scan(src); err != nil { return err } l.Valid = true return nil } golang-github-sap-go-hdb-0.14.1/driver/lobexample_test.go000066400000000000000000000047231346210761100232630ustar00rootroot00000000000000/* 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_test import ( "bytes" "database/sql" "log" "os" "github.com/SAP/go-hdb/driver" ) // ExampleLobRead reads data from a largs data object database field into a bytes.Buffer. // Precondition: the test database table with one field of type BLOB, CLOB or NCLOB must exist. // For illustrative purposes we assume, that the database table has exactly one record, so // that we can use db.QueryRow. func ExampleLob_read() { b := new(bytes.Buffer) db, err := sql.Open("hdb", "hdb://user:password@host:port") if err != nil { log.Fatal(err) } defer db.Close() lob := new(driver.Lob) lob.SetWriter(b) // SetWriter sets the io.Writer object, to which the database content of the lob field is written. if err := db.QueryRow("select * from test").Scan(lob); err != nil { log.Fatal(err) } } // ExampleLobWrite inserts data read from a file into a database large object field. // Precondition: the test database table with one field of type BLOB, CLOB or NCLOB and the // test.txt file in the working directory must exist. // Lob fields cannot be written in hdb auto commit mode - therefore the insert has to be // executed within a transaction. func ExampleLob_write() { file, err := os.Open("test.txt") // Open file. if err != nil { log.Fatal(err) } defer file.Close() db, err := sql.Open("hdb", "hdb://user:password@host:port") if err != nil { log.Fatal(err) } defer db.Close() tx, err := db.Begin() // Start Transaction to avoid database error: SQL Error 596 - LOB streaming is not permitted in auto-commit mode. if err != nil { log.Fatal(err) } stmt, err := tx.Prepare("insert into test values(?)") if err != nil { log.Fatal(err) } lob := new(driver.Lob) lob.SetReader(file) // SetReader sets the io.Reader object, which content is written to the database lob field. if _, err := stmt.Exec(lob); err != nil { log.Fatal(err) } stmt.Close() if err := tx.Commit(); err != nil { log.Fatal(err) } } golang-github-sap-go-hdb-0.14.1/driver/main_test.go000066400000000000000000000041021346210761100220460ustar00rootroot00000000000000/* 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" "flag" "fmt" "log" "os" "testing" ) // globals var ( // TestDSN (data source name for testing) has to be provided by calling go test with dsn parameter. TestDSN string // TestDropSchema could be provided by calling go test with dropSchema parameter. // If set to true (default), the test schema will be dropped after successful test execution. // If set to false, the test schema will remain on database after test execution. TestDropSchema bool // TestSchema will be used as test schema name and created on database by TestMain. // The scheam name consists of the prefix "test_" and a random Identifier. TestSchema Identifier ) func TestMain(m *testing.M) { log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile) flag.StringVar(&TestDSN, "dsn", "hdb://user:password@ip_address:port", "database dsn") flag.BoolVar(&TestDropSchema, "dropSchema", true, "drop test schema after test ran successfully") if !flag.Parsed() { flag.Parse() } // init driver db, err := sql.Open(DriverName, TestDSN) if err != nil { log.Fatal(err) } defer db.Close() // create schema TestSchema = RandomIdentifier("test_") if _, err := db.Exec(fmt.Sprintf("create schema %s", TestSchema)); err != nil { log.Fatal(err) } log.Printf("created schema %s", TestSchema) exitCode := m.Run() if exitCode == 0 && TestDropSchema { if _, err := db.Exec(fmt.Sprintf("drop schema %s cascade", TestSchema)); err != nil { log.Fatal(err) } log.Printf("dropped schema %s", TestSchema) } os.Exit(exitCode) } golang-github-sap-go-hdb-0.14.1/driver/queryexample_test.go000066400000000000000000000030001346210761100236370ustar00rootroot00000000000000/* 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" "fmt" "log" ) /* ExampleCallSimpleOut creates a stored procedure with one output parameter and executes it. Stored procedures with output parameters must be executed by sql.Query or sql.QueryRow. For variables TestDSN and TestSchema see main_test.go. */ // ExampleQuery: tbd func Example_query() { db, err := sql.Open(DriverName, TestDSN) if err != nil { log.Fatal(err) } defer db.Close() table := RandomIdentifier("testNamedArg_") if _, err := db.Exec(fmt.Sprintf("create table %s.%s (i integer, j integer)", TestSchema, table)); err != nil { log.Fatal(err) } var i = 0 if err := db.QueryRow(fmt.Sprintf("select count(*) from %s.%s where i = :1 and j = :1", TestSchema, table), 1).Scan(&i); err != nil { log.Fatal(err) } if err := db.QueryRow(fmt.Sprintf("select count(*) from %s.%s where i = ? and j = :3", TestSchema, table), 1, "soso", 2).Scan(&i); err != nil { log.Fatal(err) } fmt.Print(i) // output: 0 } golang-github-sap-go-hdb-0.14.1/driver/sqltrace/000077500000000000000000000000001346210761100213555ustar00rootroot00000000000000golang-github-sap-go-hdb-0.14.1/driver/sqltrace/doc.go000066400000000000000000000011661346210761100224550ustar00rootroot00000000000000/* 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 golang-github-sap-go-hdb-0.14.1/driver/sqltrace/example_test.go000066400000000000000000000013651346210761100244030ustar00rootroot00000000000000/* 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_test import ( "github.com/SAP/go-hdb/driver/sqltrace" ) func Example() { sqltrace.SetOn(true) //set SQL trace output active sqltrace.SetOn(false) //set SQL trace output inactive } golang-github-sap-go-hdb-0.14.1/driver/sqltrace/sqltrace.go000066400000000000000000000032151346210761100235230ustar00rootroot00000000000000/* 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...) } } // Tracef 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...) } } golang-github-sap-go-hdb-0.14.1/driver/time.go000066400000000000000000000022251346210761100210250ustar00rootroot00000000000000/* 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 } golang-github-sap-go-hdb-0.14.1/driver/tx_test.go000066400000000000000000000066071346210761100215710ustar00rootroot00000000000000/* 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" "fmt" "testing" ) func TestTransactionCommit(t *testing.T) { db1, err := sql.Open(DriverName, TestDSN) if err != nil { t.Fatal(err) } defer db1.Close() db2, err := sql.Open(DriverName, TestDSN) if err != nil { t.Fatal(err) } defer db2.Close() table := RandomIdentifier("testTxCommit_") if _, err := db1.Exec(fmt.Sprintf("create table %s.%s (i tinyint)", TestSchema, table)); err != nil { t.Fatal(err) } tx1, err := db1.Begin() if err != nil { t.Fatal(err) } tx2, err := db2.Begin() if err != nil { t.Fatal(err) } defer tx2.Rollback() //insert record in transaction 1 if _, err := tx1.Exec(fmt.Sprintf("insert into %s.%s values(42)", TestSchema, table)); err != nil { t.Fatal(err) } //count records in transaction 1 i := 0 if err := tx1.QueryRow(fmt.Sprintf("select count(*) from %s.%s", TestSchema, table)).Scan(&i); err != nil { t.Fatal(err) } if i != 1 { t.Fatal(fmt.Errorf("tx1: invalid number of records %d - 1 expected", i)) } //count records in transaction 2 - isolation level 'read committed'' (default) expected, so no record should be there if err := tx2.QueryRow(fmt.Sprintf("select count(*) from %s.%s", TestSchema, table)).Scan(&i); err != nil { t.Fatal(err) } if i != 0 { t.Fatal(fmt.Errorf("tx2: invalid number of records %d - 0 expected", i)) } //commit insert if err := tx1.Commit(); err != nil { t.Fatal(err) } //in isolation level 'read commited' (default) record should be visible now if err := tx2.QueryRow(fmt.Sprintf("select count(*) from %s.%s", TestSchema, table)).Scan(&i); err != nil { t.Fatal(err) } if i != 1 { t.Fatal(fmt.Errorf("tx2: invalid number of records %d - 1 expected", i)) } } func TestTransactionRollback(t *testing.T) { db, err := sql.Open(DriverName, TestDSN) if err != nil { t.Fatal(err) } defer db.Close() table := RandomIdentifier("testTxRollback_") if _, err := db.Exec(fmt.Sprintf("create table %s.%s (i tinyint)", TestSchema, table)); err != nil { t.Fatal(err) } tx, err := db.Begin() if err != nil { t.Fatal(err) } //insert record if _, err := tx.Exec(fmt.Sprintf("insert into %s.%s values(42)", TestSchema, table)); err != nil { t.Fatal(err) } //count records i := 0 if err := tx.QueryRow(fmt.Sprintf("select count(*) from %s.%s", TestSchema, table)).Scan(&i); err != nil { t.Fatal(err) } if i != 1 { t.Fatal(fmt.Errorf("tx: invalid number of records %d - 1 expected", i)) } //rollback insert if err := tx.Rollback(); err != nil { t.Fatal(err) } //new transaction tx, err = db.Begin() if err != nil { t.Fatal(err) } defer tx.Rollback() //rollback - no record expected if err := tx.QueryRow(fmt.Sprintf("select count(*) from %s.%s", TestSchema, table)).Scan(&i); err != nil { t.Fatal(err) } if i != 0 { t.Fatal(fmt.Errorf("tx: invalid number of records %d - 0 expected", i)) } } golang-github-sap-go-hdb-0.14.1/go.mod000066400000000000000000000000771346210761100173560ustar00rootroot00000000000000module github.com/SAP/go-hdb require golang.org/x/text v0.3.0 golang-github-sap-go-hdb-0.14.1/go.sum000066400000000000000000000002311346210761100173730ustar00rootroot00000000000000golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang-github-sap-go-hdb-0.14.1/internal/000077500000000000000000000000001346210761100200605ustar00rootroot00000000000000golang-github-sap-go-hdb-0.14.1/internal/bufio/000077500000000000000000000000001346210761100211645ustar00rootroot00000000000000golang-github-sap-go-hdb-0.14.1/internal/bufio/bufio.go000066400000000000000000000216131346210761100226220ustar00rootroot00000000000000/* 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" ) // Reader is a bufio.Reader extended by methods needed for hdb protocol. type Reader struct { rd *bufio.Reader err error b [8]byte // scratch buffer (8 Bytes) tr transform.Transformer cnt int } // NewReader creates a new Reader instance. func NewReader(r io.Reader) *Reader { return &Reader{ rd: bufio.NewReader(r), 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{ rd: bufio.NewReaderSize(r, size), tr: unicode.Cesu8ToUtf8Transformer, } } // ResetCnt resets the byte read counter. func (r *Reader) ResetCnt() { r.cnt = 0 } // Cnt returns the value of the byte read counter. func (r *Reader) Cnt() int { return r.cnt } // GetError returns reader error. func (r *Reader) GetError() error { err := r.err r.err = nil return err } // Skip skips cnt bytes from reading. func (r *Reader) Skip(cnt int) { if r.err != nil { return } var n int n, r.err = r.rd.Discard(cnt) r.cnt += n } // ReadB reads and returns a byte. func (r *Reader) ReadB() byte { // ReadB as sig differs from ReadByte (vet issues) if r.err != nil { return 0 } var b byte b, r.err = r.rd.ReadByte() r.cnt++ return b } // ReadFull implements io.ReadFull on Reader. func (r *Reader) ReadFull(p []byte) { if r.err != nil { return } var n int n, r.err = io.ReadFull(r.rd, p) r.cnt += n } // ReadBool reads and returns a boolean. func (r *Reader) ReadBool() bool { if r.err != nil { return false } return !(r.ReadB() == 0) } // ReadInt8 reads and returns an int8. func (r *Reader) ReadInt8() int8 { return int8(r.ReadB()) } // ReadInt16 reads and returns an int16. func (r *Reader) ReadInt16() int16 { if r.err != nil { return 0 } var n int n, r.err = io.ReadFull(r.rd, r.b[:2]) r.cnt += n if r.err != nil { return 0 } return int16(binary.LittleEndian.Uint16(r.b[:2])) } // ReadUint16 reads and returns an uint16. func (r *Reader) ReadUint16() uint16 { if r.err != nil { return 0 } var n int n, r.err = io.ReadFull(r.rd, r.b[:2]) r.cnt += n if r.err != nil { return 0 } return binary.LittleEndian.Uint16(r.b[:2]) } // ReadInt32 reads and returns an int32. func (r *Reader) ReadInt32() int32 { if r.err != nil { return 0 } var n int n, r.err = io.ReadFull(r.rd, r.b[:4]) r.cnt += n if r.err != nil { return 0 } return int32(binary.LittleEndian.Uint32(r.b[:4])) } // ReadUint32 reads and returns an uint32. func (r *Reader) ReadUint32() uint32 { if r.err != nil { return 0 } var n int n, r.err = io.ReadFull(r.rd, r.b[:4]) r.cnt += n if r.err != nil { return 0 } return binary.LittleEndian.Uint32(r.b[:4]) } // ReadInt64 reads and returns an int64. func (r *Reader) ReadInt64() int64 { if r.err != nil { return 0 } var n int n, r.err = io.ReadFull(r.rd, r.b[:8]) r.cnt += n if r.err != nil { return 0 } return int64(binary.LittleEndian.Uint64(r.b[:8])) } // ReadUint64 reads and returns an uint64. func (r *Reader) ReadUint64() uint64 { if r.err != nil { return 0 } var n int n, r.err = io.ReadFull(r.rd, r.b[:8]) r.cnt += n if r.err != nil { return 0 } return binary.LittleEndian.Uint64(r.b[:8]) } // ReadFloat32 reads and returns a float32. func (r *Reader) ReadFloat32() float32 { if r.err != nil { return 0 } var n int n, r.err = io.ReadFull(r.rd, r.b[:4]) r.cnt += n if r.err != nil { return 0 } bits := binary.LittleEndian.Uint32(r.b[:4]) return math.Float32frombits(bits) } // ReadFloat64 reads and returns a float64. func (r *Reader) ReadFloat64() float64 { if r.err != nil { return 0 } var n int n, r.err = io.ReadFull(r.rd, r.b[:8]) r.cnt += n if r.err != nil { return 0 } bits := binary.LittleEndian.Uint64(r.b[:8]) return math.Float64frombits(bits) } // ReadCesu8 reads a size CESU-8 encoded byte sequence and returns an UTF-8 byte slice. func (r *Reader) ReadCesu8(size int) []byte { if r.err != nil { return nil } p := make([]byte, size) var n int n, r.err = io.ReadFull(r.rd, p) r.cnt += n if r.err != nil { return nil } r.tr.Reset() if n, _, r.err = r.tr.Transform(p, p, true); r.err != nil { // inplace transformation return nil } return p[:n] } const writerBufferSize = 4096 // Writer is a bufio.Writer extended by methods needed for hdb protocol. type Writer struct { wr *bufio.Writer err error b []byte // scratch buffer (min 8 Bytes) tr transform.Transformer } // NewWriter creates a new Writer instance. func NewWriter(w io.Writer) *Writer { return &Writer{ wr: bufio.NewWriter(w), b: make([]byte, writerBufferSize), 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{ wr: bufio.NewWriterSize(w, size), b: make([]byte, writerBufferSize), tr: unicode.Utf8ToCesu8Transformer, } } // Flush writes any buffered data to the underlying io.Writer. func (w *Writer) Flush() error { if w.err != nil { return w.err } return w.wr.Flush() } // WriteZeroes writes cnt zero byte values. func (w *Writer) WriteZeroes(cnt int) { if w.err != nil { return } // 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, _ := w.wr.Write(w.b[:j]) if n != j { return } i += n } } // Write writes the contents of p. func (w *Writer) Write(p []byte) { if w.err != nil { return } w.wr.Write(p) } // WriteB writes a byte. func (w *Writer) WriteB(b byte) { // WriteB as sig differs from WriteByte (vet issues) if w.err != nil { return } w.wr.WriteByte(b) } // WriteBool writes a boolean. func (w *Writer) WriteBool(v bool) { if w.err != nil { return } if v { w.wr.WriteByte(1) } else { w.wr.WriteByte(0) } } // WriteInt8 writes an int8. func (w *Writer) WriteInt8(i int8) { if w.err != nil { return } w.wr.WriteByte(byte(i)) } // WriteInt16 writes an int16. func (w *Writer) WriteInt16(i int16) { if w.err != nil { return } binary.LittleEndian.PutUint16(w.b[:2], uint16(i)) w.wr.Write(w.b[:2]) } // WriteUint16 writes an uint16. func (w *Writer) WriteUint16(i uint16) { if w.err != nil { return } binary.LittleEndian.PutUint16(w.b[:2], i) w.wr.Write(w.b[:2]) } // WriteInt32 writes an int32. func (w *Writer) WriteInt32(i int32) { if w.err != nil { return } binary.LittleEndian.PutUint32(w.b[:4], uint32(i)) w.wr.Write(w.b[:4]) } // WriteUint32 writes an uint32. func (w *Writer) WriteUint32(i uint32) { if w.err != nil { return } binary.LittleEndian.PutUint32(w.b[:4], i) w.wr.Write(w.b[:4]) } // WriteInt64 writes an int64. func (w *Writer) WriteInt64(i int64) { if w.err != nil { return } binary.LittleEndian.PutUint64(w.b[:8], uint64(i)) w.wr.Write(w.b[:8]) } // WriteUint64 writes an uint64. func (w *Writer) WriteUint64(i uint64) { if w.err != nil { return } binary.LittleEndian.PutUint64(w.b[:8], i) w.wr.Write(w.b[:8]) } // WriteFloat32 writes a float32. func (w *Writer) WriteFloat32(f float32) { if w.err != nil { return } bits := math.Float32bits(f) binary.LittleEndian.PutUint32(w.b[:4], bits) w.wr.Write(w.b[:4]) } // WriteFloat64 writes a float64. func (w *Writer) WriteFloat64(f float64) { if w.err != nil { return } bits := math.Float64bits(f) binary.LittleEndian.PutUint64(w.b[:8], bits) w.wr.Write(w.b[:8]) } // WriteString writes a string. func (w *Writer) WriteString(s string) { if w.err != nil { return } w.wr.WriteString(s) } // WriteCesu8 writes an UTF-8 byte slice as CESU-8 and returns the CESU-8 bytes written. func (w *Writer) WriteCesu8(p []byte) int { if w.err != nil { return 0 } 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 { w.err = err return cnt } if m == 0 { w.err = transform.ErrShortDst return cnt } o, _ := w.wr.Write(w.b[:m]) cnt += o i += n } return cnt } // WriteStringCesu8 is like WriteCesu8 with an UTF-8 string as parameter. func (w *Writer) WriteStringCesu8(s string) int { return w.WriteCesu8([]byte(s)) } golang-github-sap-go-hdb-0.14.1/internal/protocol/000077500000000000000000000000001346210761100217215ustar00rootroot00000000000000golang-github-sap-go-hdb-0.14.1/internal/protocol/clientid.go000066400000000000000000000022461346210761100240470ustar00rootroot00000000000000/* 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(pkClientID) } 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 { wr.Write(id) if trace { outLogger.Printf("client id: %s", id) } return nil } golang-github-sap-go-hdb-0.14.1/internal/protocol/command.go000066400000000000000000000017671346210761100237010ustar00rootroot00000000000000/* 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 { wr.WriteCesu8(c) if trace { outLogger.Printf("command: %s", c) } return nil } golang-github-sap-go-hdb-0.14.1/internal/protocol/connectoption.go000066400000000000000000000043641346210761100251410ustar00rootroot00000000000000/* 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 // duplicate in docu: coDataFormatVersion2 connectOption = 5 // 6-9 reserved: do not use coLargeNumberOfParameterSupport connectOption = 10 coSystemID connectOption = 11 // 12 reserved: do not use coAbapVarcharMode connectOption = 13 coSelectForUpdateSupported connectOption = 14 coClientDistributionMode connectOption = 15 coEngineDataFormatVersion connectOption = 16 coDistributionProtocolVersion connectOption = 17 coSplitBatchCommands connectOption = 18 coUseTransactionFlagsOnly connectOption = 19 //coRowAndColumnOptimizedFormat connectOption = 20 reserved: do not use coIgnoreUnknownParts connectOption = 21 coTableOutputParameter connectOption = 22 coDataFormatVersion2 connectOption = 23 coItabParameter connectOption = 24 coDescribeTableOutputParameter connectOption = 25 coColumnarResultset connectOption = 26 coScrollablResultSet connectOption = 27 coClientInfoNullValueSupported connectOption = 28 coAssociatedConnectionID connectOption = 29 coNoTransactionalPrepare connectOption = 30 coFDAEnabled connectOption = 31 coOSUser connectOption = 32 coRowslotImageResult connectOption = 33 coEndianess connectOption = 34 // 35, 36 reserved: do not use coImplicitLobStreaming connectOption = 37 ) golang-github-sap-go-hdb-0.14.1/internal/protocol/connectoption_string.go000066400000000000000000000034011346210761100265160ustar00rootroot00000000000000// Code generated by "stringer -type=connectOption"; DO NOT EDIT. package protocol import "strconv" const ( _connectOption_name_0 = "coConnectionIDcoCompleteArrayExecutioncoClientLocalecoSupportsLargeBulkOperations" _connectOption_name_1 = "coLargeNumberOfParameterSupportcoSystemID" _connectOption_name_2 = "coAbapVarcharModecoSelectForUpdateSupportedcoClientDistributionModecoEngineDataFormatVersioncoDistributionProtocolVersioncoSplitBatchCommandscoUseTransactionFlagsOnly" _connectOption_name_3 = "coIgnoreUnknownPartscoTableOutputParametercoDataFormatVersion2coItabParametercoDescribeTableOutputParametercoColumnarResultsetcoScrollablResultSetcoClientInfoNullValueSupportedcoAssociatedConnectionIDcoNoTransactionalPreparecoFDAEnabledcoOSUsercoRowslotImageResultcoEndianess" _connectOption_name_4 = "coImplicitLobStreaming" ) var ( _connectOption_index_0 = [...]uint8{0, 14, 38, 52, 81} _connectOption_index_1 = [...]uint8{0, 31, 41} _connectOption_index_2 = [...]uint8{0, 17, 43, 67, 92, 121, 141, 166} _connectOption_index_3 = [...]uint16{0, 20, 42, 62, 77, 107, 126, 146, 176, 200, 224, 236, 244, 264, 275} ) 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 <= 11: i -= 10 return _connectOption_name_1[_connectOption_index_1[i]:_connectOption_index_1[i+1]] case 13 <= i && i <= 19: i -= 13 return _connectOption_name_2[_connectOption_index_2[i]:_connectOption_index_2[i+1]] case 21 <= i && i <= 34: i -= 21 return _connectOption_name_3[_connectOption_index_3[i]:_connectOption_index_3[i+1]] case i == 37: return _connectOption_name_4 default: return "connectOption(" + strconv.FormatInt(int64(i), 10) + ")" } } golang-github-sap-go-hdb-0.14.1/internal/protocol/connectoptions.go000066400000000000000000000042401346210761100253150ustar00rootroot00000000000000/* 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 intType = 3 dfvSPS06 intType = 4 //see docu dfvBINTEXT intType = 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 { o.po.read(rd, o._numArg) if trace { outLogger.Printf("connect options: %v", o) } return rd.GetError() } func (o *connectOptions) write(wr *bufio.Writer) error { o.po.write(wr) if trace { outLogger.Printf("connect options: %v", o) } return nil } golang-github-sap-go-hdb-0.14.1/internal/protocol/datatype.go000066400000000000000000000016011346210761100240610ustar00rootroot00000000000000/* 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 DtInteger DtBigint DtReal DtDouble DtDecimal DtTime DtString DtBytes DtLob ) golang-github-sap-go-hdb-0.14.1/internal/protocol/datatype_string.go000066400000000000000000000010011346210761100254410ustar00rootroot00000000000000// Code generated by "stringer -type=DataType"; DO NOT EDIT. package protocol import "strconv" const _DataType_name = "DtUnknownDtTinyintDtSmallintDtIntegerDtBigintDtRealDtDoubleDtDecimalDtTimeDtStringDtBytesDtLob" var _DataType_index = [...]uint8{0, 9, 18, 28, 37, 45, 51, 59, 68, 74, 82, 89, 94} func (i DataType) String() string { if i >= DataType(len(_DataType_index)-1) { return "DataType(" + strconv.FormatInt(int64(i), 10) + ")" } return _DataType_name[_DataType_index[i]:_DataType_index[i+1]] } golang-github-sap-go-hdb-0.14.1/internal/protocol/doc.go000066400000000000000000000013221346210761100230130ustar00rootroot00000000000000/* 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 golang-github-sap-go-hdb-0.14.1/internal/protocol/endianess.go000066400000000000000000000012731346210761100242240ustar00rootroot00000000000000/* 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 ) golang-github-sap-go-hdb-0.14.1/internal/protocol/endianess_string.go000066400000000000000000000006431346210761100256120ustar00rootroot00000000000000// Code generated by "stringer -type=endianess"; DO NOT EDIT. package protocol import "strconv" 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 "endianess(" + strconv.FormatInt(int64(i), 10) + ")" } return _endianess_name[_endianess_index[i]:_endianess_index[i+1]] } golang-github-sap-go-hdb-0.14.1/internal/protocol/error.go000066400000000000000000000126361346210761100234110ustar00rootroot00000000000000/* 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 //bytes of fix length fields mod 8 // - errorCode = 4, errorPosition = 4, errortextLength = 4, errorLevel = 1, sqlState = 5 => 18 bytes // - 18 mod 8 = 2 fixLength = 2 ) type sqlState [sqlStateSize]byte type hdbError struct { errorCode int32 errorPosition int32 errorTextLength int32 errorLevel errorLevel sqlState sqlState stmtNo int errorText []byte } // String implements the Stringer interface. func (e *hdbError) String() string { return fmt.Sprintf("errorCode %d, errorPosition %d, errorTextLength % d errorLevel %s, sqlState %s stmtNo %d errorText %s", e.errorCode, e.errorPosition, e.errorTextLength, e.errorLevel, e.sqlState, e.stmtNo, e.errorText, ) } // Error implements the Error interface. func (e *hdbError) Error() string { if e.stmtNo != -1 { return fmt.Sprintf("SQL %s %d - %s (statement no: %d)", e.errorLevel, e.errorCode, e.errorText, e.stmtNo) } return fmt.Sprintf("SQL %s %d - %s", e.errorLevel, e.errorCode, e.errorText) } type hdbErrors struct { errors []*hdbError numArg int idx int } // String implements the Stringer interface. func (e *hdbErrors) String() string { return e.errors[e.idx].String() } // Error implements the golang error interface. func (e *hdbErrors) Error() string { return e.errors[e.idx].Error() } // NumError implements the driver.Error interface. func (e *hdbErrors) NumError() int { return e.numArg } // SetIdx implements the driver.Error interface. func (e *hdbErrors) SetIdx(idx int) { switch { case idx < 0: e.idx = 0 case idx >= e.numArg: e.idx = e.numArg - 1 default: e.idx = idx } } // StmtNo implements the driver.Error interface. func (e *hdbErrors) StmtNo() int { return e.errors[e.idx].stmtNo } // Code implements the driver.Error interface. func (e *hdbErrors) Code() int { return int(e.errors[e.idx].errorCode) } // Position implements the driver.Error interface. func (e *hdbErrors) Position() int { return int(e.errors[e.idx].errorPosition) } // Level implements the driver.Error interface. func (e *hdbErrors) Level() int { return int(e.errors[e.idx].errorLevel) } // Text implements the driver.Error interface. func (e *hdbErrors) Text() string { return string(e.errors[e.idx].errorText) } // IsWarning implements the driver.Error interface. func (e *hdbErrors) IsWarning() bool { return e.errors[e.idx].errorLevel == errorLevelWarning } // IsError implements the driver.Error interface. func (e *hdbErrors) IsError() bool { return e.errors[e.idx].errorLevel == errorLevelError } // IsFatal implements the driver.Error interface. func (e *hdbErrors) IsFatal() bool { return e.errors[e.idx].errorLevel == errorLevelFatalError } func (e *hdbErrors) setStmtNo(idx, no int) { if idx >= 0 && idx < e.numArg { e.errors[idx].stmtNo = no } } func (e *hdbErrors) isWarnings() bool { for _, _error := range e.errors { if _error.errorLevel != errorLevelWarning { return false } } return true } func (e *hdbErrors) kind() partKind { return pkError } func (e *hdbErrors) setNumArg(numArg int) { e.numArg = numArg } func (e *hdbErrors) read(rd *bufio.Reader) error { e.idx = 0 // init error index if e.errors == nil || e.numArg > cap(e.errors) { e.errors = make([]*hdbError, e.numArg) } else { e.errors = e.errors[:e.numArg] } for i := 0; i < e.numArg; i++ { _error := e.errors[i] if _error == nil { _error = new(hdbError) e.errors[i] = _error } _error.stmtNo = -1 _error.errorCode = rd.ReadInt32() _error.errorPosition = rd.ReadInt32() _error.errorTextLength = rd.ReadInt32() _error.errorLevel = errorLevel(rd.ReadInt8()) rd.ReadFull(_error.sqlState[:]) // 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 // } _error.errorText = make([]byte, int(_error.errorTextLength)) rd.ReadFull(_error.errorText) if trace { outLogger.Printf("error %d: %s", i, _error) } if e.numArg == 1 { // Error (protocol error?): // if only one error (numArg == 1): s.ph.bufferLength is one byte greater than data to be read // if more than one error: s.ph.bufferlength matches read bytes + padding // // Examples: // driver test TestHDBWarning // --> 18 bytes fix error bytes + 103 bytes error text => 121 bytes (7 bytes padding needed) // but s.ph.bufferLength = 122 (standard padding would only consume 6 bytes instead of 7) // driver test TestBulkInsertDuplicates // --> returns 3 errors (number of total bytes matches s.ph.bufferLength) rd.Skip(1) break } pad := padBytes(int(fixLength + _error.errorTextLength)) if pad != 0 { rd.Skip(pad) } } return rd.GetError() } golang-github-sap-go-hdb-0.14.1/internal/protocol/errorlevel.go000066400000000000000000000016661346210761100244420ustar00rootroot00000000000000/* 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 // ErrorLevel send from database server. type errorLevel int8 func (e errorLevel) String() string { switch e { case 0: return "Warning" case 1: return "Error" case 2: return "Fatal Error" default: return "" } } // HDB error level constants. const ( errorLevelWarning errorLevel = 0 errorLevelError errorLevel = 1 errorLevelFatalError errorLevel = 2 ) golang-github-sap-go-hdb-0.14.1/internal/protocol/fetchsize.go000066400000000000000000000017131346210761100242360ustar00rootroot00000000000000/* 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 { wr.WriteInt32(int32(s)) if trace { outLogger.Printf("fetchsize: %d", s) } return nil } golang-github-sap-go-hdb-0.14.1/internal/protocol/field.go000066400000000000000000000416401346210761100233400ustar00rootroot00000000000000/* 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" ) var test uint32 const ( realNullValue uint32 = ^uint32(0) doubleNullValue uint64 = ^uint64(0) ) const noFieldName uint32 = 0xFFFFFFFF 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 fieldNames map[uint32]string func newFieldNames() fieldNames { return make(map[uint32]string) } func (f fieldNames) addOffset(offset uint32) { if offset != noFieldName { f[offset] = "" } } func (f fieldNames) name(offset uint32) string { if name, ok := f[offset]; ok { return name } return "" } func (f fieldNames) setName(offset uint32, name string) { f[offset] = name } func (f fieldNames) sortOffsets() []uint32 { offsets := make([]uint32, 0, len(f)) for k := range f { offsets = append(offsets, k) } uint32Slice(offsets).sort() return offsets } // FieldValues contains rows read from database. type FieldValues struct { rows int cols int values []driver.Value } func newFieldValues() *FieldValues { return &FieldValues{} } func (f *FieldValues) String() string { return fmt.Sprintf("rows %d columns %d", f.rows, f.cols) } func (f *FieldValues) resize(rows, cols int) { f.rows, f.cols = rows, cols f.values = make([]driver.Value, rows*cols) } // 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]) } const ( tinyintFieldSize = 1 smallintFieldSize = 2 intFieldSize = 4 bigintFieldSize = 8 realFieldSize = 4 doubleFieldSize = 8 dateFieldSize = 4 timeFieldSize = 4 timestampFieldSize = dateFieldSize + timeFieldSize longdateFieldSize = 8 seconddateFieldSize = 8 daydateFieldSize = 4 secondtimeFieldSize = 4 decimalFieldSize = 16 lobInputDescriptorSize = 9 ) func fieldSize(tc TypeCode, arg driver.NamedValue) (int, error) { v := arg.Value if v == nil { //HDB bug: secondtime null value --> see writeField return 0, nil } switch tc { case tcTinyint: return tinyintFieldSize, nil case tcSmallint: return smallintFieldSize, nil case tcInteger: 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 tcLongdate: return longdateFieldSize, nil case tcSeconddate: return seconddateFieldSize, nil case tcDaydate: return daydateFieldSize, nil case tcSecondtime: return secondtimeFieldSize, 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(session *Session, rd *bufio.Reader, tc TypeCode) (interface{}, error) { switch tc { case tcTinyint, tcSmallint, tcInteger, tcBigint: if !rd.ReadBool() { //null value return nil, nil } switch tc { case tcTinyint: return int64(rd.ReadB()), nil case tcSmallint: return int64(rd.ReadInt16()), nil case tcInteger: return int64(rd.ReadInt32()), nil case tcBigint: return rd.ReadInt64(), nil } case tcReal: v := rd.ReadUint32() if v == realNullValue { return nil, nil } return float64(math.Float32frombits(v)), nil case tcDouble: v := rd.ReadUint64() if v == doubleNullValue { return nil, nil } return math.Float64frombits(v), nil case tcDate: year, month, day, null := readDate(rd) 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 := readTime(rd) if null { return nil, nil } return time.Date(1, 1, 1, hour, minute, 0, nanosecs, time.UTC), nil case tcTimestamp: year, month, day, dateNull := readDate(rd) hour, minute, nanosecs, timeNull := readTime(rd) if dateNull || timeNull { return nil, nil } return time.Date(year, month, day, hour, minute, 0, nanosecs, time.UTC), nil case tcLongdate: time, null := readLongdate(rd) if null { return nil, nil } return time, nil case tcSeconddate: time, null := readSeconddate(rd) if null { return nil, nil } return time, nil case tcDaydate: time, null := readDaydate(rd) if null { return nil, nil } return time, nil case tcSecondtime: time, null := readSecondtime(rd) if null { return nil, nil } return time, nil case tcDecimal: b, null := readDecimal(rd) if null { return nil, nil } return b, nil case tcChar, tcVarchar: value, null := readBytes(rd) if null { return nil, nil } return value, nil case tcNchar, tcNvarchar: value, null := readUtf8(rd) if null { return nil, nil } return value, nil case tcBinary, tcVarbinary: value, null := readBytes(rd) if null { return nil, nil } return value, nil case tcBlob, tcClob, tcNclob: null, writer, err := readLob(session, rd, tc) if null { return nil, nil } return writer, err } outLogger.Fatalf("read field: type code %s not implemented", tc) return nil, nil } func writeField(wr *bufio.Writer, tc TypeCode, arg driver.NamedValue) error { v := arg.Value //HDB bug: secondtime null value cannot be set by setting high byte // trying so, gives // SQL HdbError 1033 - error while parsing protocol: no such data type: type_code=192, index=2 // null value //if v == nil && tc != tcSecondtime if v == nil { wr.WriteB(byte(tc) | 0x80) //set high bit return nil } // type code wr.WriteB(byte(tc)) switch tc { default: outLogger.Fatalf("write field: type code %s not implemented", tc) case tcTinyint, tcSmallint, tcInteger, tcBigint: var i64 int64 switch v := v.(type) { default: return fmt.Errorf("invalid argument type %T", v) case bool: if v { i64 = 1 } else { i64 = 0 } case int64: i64 = v } switch tc { case tcTinyint: wr.WriteB(byte(i64)) case tcSmallint: wr.WriteInt16(int16(i64)) case tcInteger: wr.WriteInt32(int32(i64)) case tcBigint: wr.WriteInt64(i64) } case tcReal: f64, ok := v.(float64) if !ok { return fmt.Errorf("invalid argument type %T", v) } wr.WriteFloat32(float32(f64)) case tcDouble: f64, ok := v.(float64) if !ok { return fmt.Errorf("invalid argument type %T", v) } wr.WriteFloat64(f64) case tcDate: t, ok := v.(time.Time) if !ok { return fmt.Errorf("invalid argument type %T", v) } writeDate(wr, t) case tcTime: t, ok := v.(time.Time) if !ok { return fmt.Errorf("invalid argument type %T", v) } writeTime(wr, t) case tcTimestamp: t, ok := v.(time.Time) if !ok { return fmt.Errorf("invalid argument type %T", v) } writeDate(wr, t) writeTime(wr, t) case tcLongdate: t, ok := v.(time.Time) if !ok { return fmt.Errorf("invalid argument type %T", v) } writeLongdate(wr, t) case tcSeconddate: t, ok := v.(time.Time) if !ok { return fmt.Errorf("invalid argument type %T", v) } writeSeconddate(wr, t) case tcDaydate: t, ok := v.(time.Time) if !ok { return fmt.Errorf("invalid argument type %T", v) } writeDaydate(wr, t) case tcSecondtime: // HDB bug: write null value explicite if v == nil { wr.WriteInt32(86401) return nil } t, ok := v.(time.Time) if !ok { return fmt.Errorf("invalid argument type %T", v) } writeSecondtime(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) } wr.Write(b) case tcChar, tcVarchar, tcString: switch v := v.(type) { case []byte: writeBytes(wr, v) case string: writeString(wr, v) default: return fmt.Errorf("invalid argument type %T", v) } case tcNchar, tcNvarchar, tcNstring: switch v := v.(type) { case []byte: writeUtf8Bytes(wr, v) case string: 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) } writeBytes(wr, v) case tcBlob, tcClob, tcNclob: writeLob(wr) } 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) { year := rd.ReadUint16() null := ((year & 0x8000) == 0) //null value year &= 0x3fff month := rd.ReadInt8() month++ day := rd.ReadInt8() return int(year), time.Month(month), int(day), null } // year: set most sig bit // month 0 based func writeDate(wr *bufio.Writer, t time.Time) { //store in utc utc := t.In(time.UTC) year, month, day := utc.Date() wr.WriteUint16(uint16(year) | 0x8000) wr.WriteInt8(int8(month) - 1) wr.WriteInt8(int8(day)) } func readTime(rd *bufio.Reader) (int, int, int, bool) { hour := rd.ReadB() null := (hour & 0x80) == 0 //null value hour &= 0x7f minute := rd.ReadInt8() millisecs := rd.ReadUint16() nanosecs := int(millisecs) * 1000000 return int(hour), int(minute), nanosecs, null } func writeTime(wr *bufio.Writer, t time.Time) { //store in utc utc := t.UTC() wr.WriteB(byte(utc.Hour()) | 0x80) wr.WriteInt8(int8(utc.Minute())) millisecs := utc.Second()*1000 + utc.Round(time.Millisecond).Nanosecond()/1000000 wr.WriteUint16(uint16(millisecs)) } var zeroTime = time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC) func readLongdate(rd *bufio.Reader) (time.Time, bool) { longdate := rd.ReadInt64() if longdate == 3155380704000000001 { // null value return zeroTime, true } return convertLongdateToTime(longdate), false } func writeLongdate(wr *bufio.Writer, t time.Time) { wr.WriteInt64(convertTimeToLongdate(t)) } func readSeconddate(rd *bufio.Reader) (time.Time, bool) { seconddate := rd.ReadInt64() if seconddate == 315538070401 { // null value return zeroTime, true } return convertSeconddateToTime(seconddate), false } func writeSeconddate(wr *bufio.Writer, t time.Time) { wr.WriteInt64(convertTimeToSeconddate(t)) } func readDaydate(rd *bufio.Reader) (time.Time, bool) { daydate := rd.ReadInt32() if daydate == 3652062 { // null value return zeroTime, true } return convertDaydateToTime(int64(daydate)), false } func writeDaydate(wr *bufio.Writer, t time.Time) { wr.WriteInt32(int32(convertTimeToDayDate(t))) } func readSecondtime(rd *bufio.Reader) (time.Time, bool) { secondtime := rd.ReadInt32() if secondtime == 86401 { // null value return zeroTime, true } return convertSecondtimeToTime(int(secondtime)), false } func writeSecondtime(wr *bufio.Writer, t time.Time) { wr.WriteInt32(int32(convertTimeToSecondtime(t))) } // nanosecond: HDB - 7 digits precision (not 9 digits) func convertTimeToLongdate(t time.Time) int64 { t = t.UTC() return (((((((int64(convertTimeToDayDate(t))-1)*24)+int64(t.Hour()))*60)+int64(t.Minute()))*60)+int64(t.Second()))*10000000 + int64(t.Nanosecond()/100) + 1 } func convertLongdateToTime(longdate int64) time.Time { const dayfactor = 10000000 * 24 * 60 * 60 longdate-- d := (longdate % dayfactor) * 100 t := convertDaydateToTime((longdate / dayfactor) + 1) return t.Add(time.Duration(d)) } func convertTimeToSeconddate(t time.Time) int64 { t = t.UTC() return (((((int64(convertTimeToDayDate(t))-1)*24)+int64(t.Hour()))*60)+int64(t.Minute()))*60 + int64(t.Second()) + 1 } func convertSeconddateToTime(seconddate int64) time.Time { const dayfactor = 24 * 60 * 60 seconddate-- d := (seconddate % dayfactor) * 1000000000 t := convertDaydateToTime((seconddate / dayfactor) + 1) return t.Add(time.Duration(d)) } const julianHdb = 1721423 // 1 January 0001 00:00:00 (1721424) - 1 func convertTimeToDayDate(t time.Time) int64 { return int64(timeToJulianDay(t) - julianHdb) } func convertDaydateToTime(daydate int64) time.Time { return julianDayToTime(int(daydate) + julianHdb) } func convertTimeToSecondtime(t time.Time) int { t = t.UTC() return (t.Hour()*60+t.Minute())*60 + t.Second() + 1 } func convertSecondtimeToTime(secondtime int) time.Time { return time.Date(1, 1, 1, 0, 0, 0, 0, time.UTC).Add(time.Duration(int64(secondtime-1) * 1000000000)) } func readDecimal(rd *bufio.Reader) ([]byte, bool) { b := make([]byte, 16) rd.ReadFull(b) if (b[15] & 0x70) == 0x70 { //null value (bit 4,5,6 set) return nil, true } return b, false } // 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) { ind := rd.ReadB() //length indicator switch { default: return 0, false case ind == bytesLenIndNullValue: return 0, true case ind <= bytesLenIndSmall: return int(ind), false case ind == bytesLenIndMedium: return int(rd.ReadInt16()), false case ind == bytesLenIndBig: return int(rd.ReadInt32()), false } } 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): wr.WriteB(byte(size)) case size <= math.MaxInt16: wr.WriteB(bytesLenIndMedium) wr.WriteInt16(int16(size)) case size <= math.MaxInt32: wr.WriteB(bytesLenIndBig) wr.WriteInt32(int32(size)) } return nil } func readBytes(rd *bufio.Reader) ([]byte, bool) { size, null := readBytesSize(rd) if null { return nil, true } b := make([]byte, size) rd.ReadFull(b) return b, false } func readUtf8(rd *bufio.Reader) ([]byte, bool) { size, null := readBytesSize(rd) if null { return nil, true } b := rd.ReadCesu8(size) return b, false } // strings with one byte length func readShortUtf8(rd *bufio.Reader) ([]byte, int) { size := rd.ReadB() b := rd.ReadCesu8(int(size)) return b, int(size) } func writeBytes(wr *bufio.Writer, b []byte) { writeBytesSize(wr, len(b)) wr.Write(b) } func writeString(wr *bufio.Writer, s string) { writeBytesSize(wr, len(s)) wr.WriteString(s) } func writeUtf8Bytes(wr *bufio.Writer, b []byte) { size := cesu8.Size(b) writeBytesSize(wr, size) wr.WriteCesu8(b) } func writeUtf8String(wr *bufio.Writer, s string) { size := cesu8.StringSize(s) writeBytesSize(wr, size) wr.WriteStringCesu8(s) } func readLob(s *Session, rd *bufio.Reader, tc TypeCode) (bool, lobChunkWriter, error) { rd.ReadInt8() // type code (is int here) opt := rd.ReadInt8() null := (lobOptions(opt) & loNullindicator) != 0 if null { return true, nil, nil } eof := (lobOptions(opt) & loLastdata) != 0 rd.Skip(2) charLen := rd.ReadInt64() byteLen := rd.ReadInt64() id := rd.ReadUint64() chunkLen := rd.ReadInt32() lobChunkWriter := newLobChunkWriter(tc.isCharBased(), s, locatorID(id), charLen, byteLen) if err := lobChunkWriter.write(rd, int(chunkLen), eof); err != nil { return null, lobChunkWriter, err } return null, lobChunkWriter, nil } // TODO: first write: add content? - actually no data transferred func writeLob(wr *bufio.Writer) { wr.WriteB(0) wr.WriteInt32(0) wr.WriteInt32(0) } golang-github-sap-go-hdb-0.14.1/internal/protocol/functioncode.go000066400000000000000000000037011346210761100247310ustar00rootroot00000000000000/* 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 } } golang-github-sap-go-hdb-0.14.1/internal/protocol/functioncode_string.go000066400000000000000000000014101346210761100263120ustar00rootroot00000000000000// Code generated by "stringer -type=functionCode"; DO NOT EDIT. package protocol import "strconv" 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 "functionCode(" + strconv.FormatInt(int64(i), 10) + ")" } return _functionCode_name[_functionCode_index[i]:_functionCode_index[i+1]] } golang-github-sap-go-hdb-0.14.1/internal/protocol/init.go000066400000000000000000000075461346210761100232270ustar00rootroot00000000000000/* 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 { rd.Skip(initRequestFillerSize) //filler r.product.major = rd.ReadInt8() r.product.minor = rd.ReadInt16() r.protocol.major = rd.ReadInt8() r.protocol.minor = rd.ReadInt16() rd.Skip(1) //reserved filler r.numOptions = rd.ReadInt8() switch r.numOptions { default: outLogger.Fatalf("invalid number of options %d", r.numOptions) case 0: rd.Skip(2) case 1: cnt := rd.ReadInt8() if cnt != 1 { outLogger.Fatalf("endianess %d - 1 expected", cnt) } r.endianess = endianess(rd.ReadInt8()) } if trace { outLogger.Printf("read %s", r) } return rd.GetError() } func (r *initRequest) write(wr *bufio.Writer) error { wr.WriteUint32(initRequestFiller) wr.WriteInt8(r.product.major) wr.WriteInt16(r.product.minor) wr.WriteInt8(r.protocol.major) wr.WriteInt16(r.protocol.minor) switch r.numOptions { default: outLogger.Fatalf("invalid number of options %d", r.numOptions) case 0: wr.WriteZeroes(4) case 1: // reserved wr.WriteZeroes(1) wr.WriteInt8(r.numOptions) wr.WriteInt8(int8(okEndianess)) wr.WriteInt8(int8(r.endianess)) } // 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 { r.product.major = rd.ReadInt8() r.product.minor = rd.ReadInt16() r.protocol.major = rd.ReadInt8() r.protocol.minor = rd.ReadInt16() rd.Skip(2) //commitInitReplySize if trace { outLogger.Printf("read %s", r) } return rd.GetError() } func (r *initReply) write(wr *bufio.Writer) error { wr.WriteInt8(r.product.major) wr.WriteInt16(r.product.minor) wr.WriteInt8(r.product.major) wr.WriteInt16(r.protocol.minor) wr.WriteZeroes(2) // commitInitReplySize // flush if err := wr.Flush(); err != nil { return err } if trace { outLogger.Printf("write %s", r) } return nil } golang-github-sap-go-hdb-0.14.1/internal/protocol/lob.go000066400000000000000000000245631346210761100230360ustar00rootroot00000000000000/* 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 = 4096 //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) } type locatorID uint64 // byte[locatorIdSize] // write lob reply type writeLobReply struct { ids []locatorID numArg int } 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 r.ids == nil || 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++ { r.ids[i] = locatorID(rd.ReadUint64()) } return rd.GetError() } //write lob request type writeLobRequest struct { lobPrmFields []*ParameterField } func (r *writeLobRequest) kind() partKind { return pkWriteLobRequest } func (r *writeLobRequest) size() (int, error) { // TODO: check size limit size := 0 for _, prmField := range r.lobPrmFields { cr := prmField.chunkReader if cr.done() { continue } if err := cr.fill(); err != nil { return 0, err } size += writeLobRequestHeaderSize size += cr.size() } return size, nil } func (r *writeLobRequest) numArg() int { n := 0 for _, prmField := range r.lobPrmFields { cr := prmField.chunkReader if !cr.done() { n++ } } return n } func (r *writeLobRequest) write(wr *bufio.Writer) error { for _, prmField := range r.lobPrmFields { cr := prmField.chunkReader if !cr.done() { wr.WriteUint64(uint64(prmField.lobLocatorID)) opt := int8(0x02) // data included if cr.eof() { opt |= 0x04 // last data } wr.WriteInt8(opt) wr.WriteInt64(-1) //offset (-1 := append) wr.WriteInt32(int32(cr.size())) // size wr.Write(cr.bytes()) } } return nil } //read lob request type readLobRequest struct { w lobChunkWriter } func (r *readLobRequest) kind() partKind { return pkReadLobRequest } func (r *readLobRequest) size() (int, error) { return readLobRequestSize, nil } func (r *readLobRequest) numArg() int { return 1 } func (r *readLobRequest) write(wr *bufio.Writer) error { wr.WriteUint64(uint64(r.w.id())) readOfs, readLen := r.w.readOfsLen() wr.WriteInt64(readOfs + 1) //1-based wr.WriteInt32(readLen) wr.WriteZeroes(4) return nil } // read lob reply // - seems like readLobreply gives only an result for one lob - even if more then one is requested // --> read single lobs type readLobReply struct { w lobChunkWriter } func (r *readLobReply) kind() partKind { return pkReadLobReply } func (r *readLobReply) setNumArg(numArg int) { if numArg != 1 { panic("numArg == 1 expected") } } func (r *readLobReply) read(rd *bufio.Reader) error { id := rd.ReadUint64() if r.w.id() != locatorID(id) { return fmt.Errorf("internal error: invalid lob locator %d - expected %d", id, r.w.id()) } opt := rd.ReadInt8() chunkLen := rd.ReadInt32() rd.Skip(3) eof := (lobOptions(opt) & loLastdata) != 0 if err := r.w.write(rd, int(chunkLen), eof); err != nil { return err } return rd.GetError() } // lobChunkReader reads lob field io.Reader in chunks for writing to db. type lobChunkReader interface { fill() error size() int bytes() []byte eof() bool done() bool } func newLobChunkReader(isCharBased bool, r io.Reader) lobChunkReader { if isCharBased { return &charLobChunkReader{r: r} } return &binaryLobChunkReader{r: r} } // binaryLobChunkReader (byte based chunks). type binaryLobChunkReader struct { r io.Reader _size int _eof bool _done bool b []byte } func (l *binaryLobChunkReader) eof() bool { return l._eof } func (l *binaryLobChunkReader) done() bool { return l._done } func (l *binaryLobChunkReader) size() int { return l._size } func (l *binaryLobChunkReader) bytes() []byte { l._done = l._eof return l.b[:l._size] } func (l *binaryLobChunkReader) fill() error { if l._eof { return io.EOF } 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 } // charLobChunkReader (cesu8 character based chunks). type charLobChunkReader struct { r io.Reader _size int _eof bool _done bool b []byte c []byte ofs int } func (l *charLobChunkReader) eof() bool { return l._eof } func (l *charLobChunkReader) done() bool { return l._done } func (l *charLobChunkReader) size() int { return l._size } func (l *charLobChunkReader) bytes() []byte { l._done = l._eof return l.b[:l._size] } func (l *charLobChunkReader) fill() error { if l._eof { return io.EOF } 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 } // lobChunkWriter reads db lob chunks and writes them into lob field io.Writer. type lobChunkWriter interface { SetWriter(w io.Writer) error // gets called by driver.Lob.Scan id() locatorID write(rd *bufio.Reader, size int, eof bool) error readOfsLen() (int64, int32) eof() bool } func newLobChunkWriter(isCharBased bool, s *Session, id locatorID, charLen, byteLen int64) lobChunkWriter { if isCharBased { return &charLobChunkWriter{s: s, _id: id, charLen: charLen, byteLen: byteLen} } return &binaryLobChunkWriter{s: s, _id: id, charLen: charLen, byteLen: byteLen} } // binaryLobChunkWriter (byte based lobs). type binaryLobChunkWriter struct { s *Session _id locatorID charLen int64 byteLen int64 readOfs int64 _eof bool ofs int wr io.Writer b []byte } func (l *binaryLobChunkWriter) id() locatorID { return l._id } func (l *binaryLobChunkWriter) eof() bool { return l._eof } func (l *binaryLobChunkWriter) SetWriter(wr io.Writer) error { l.wr = wr if err := l.flush(); err != nil { return err } return l.s.readLobStream(l) } func (l *binaryLobChunkWriter) 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) rd.ReadFull(l.b[l.ofs:]) if l.wr != nil { return l.flush() } return nil } func (l *binaryLobChunkWriter) 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) } func (l *binaryLobChunkWriter) flush() error { if _, err := l.wr.Write(l.b); err != nil { return err } l.readOfs += int64(len(l.b)) return nil } type charLobChunkWriter struct { s *Session _id locatorID charLen int64 byteLen int64 readOfs int64 _eof bool ofs int wr io.Writer b []byte } func (l *charLobChunkWriter) id() locatorID { return l._id } func (l *charLobChunkWriter) eof() bool { return l._eof } func (l *charLobChunkWriter) SetWriter(wr io.Writer) error { l.wr = wr if err := l.flush(); err != nil { return err } return l.s.readLobStream(l) } func (l *charLobChunkWriter) 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) rd.ReadFull(l.b[l.ofs:]) if l.wr != nil { return l.flush() } return nil } func (l *charLobChunkWriter) 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) } func (l *charLobChunkWriter) 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 *charLobChunkWriter) 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 } // 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] } golang-github-sap-go-hdb-0.14.1/internal/protocol/message.go000066400000000000000000000032561346210761100237020ustar00rootroot00000000000000/* 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 { wr.WriteInt64(h.sessionID) wr.WriteInt32(h.packetCount) wr.WriteUint32(h.varPartLength) wr.WriteUint32(h.varPartSize) wr.WriteInt16(h.noOfSegm) wr.WriteZeroes(10) //messageHeaderSize if trace { outLogger.Printf("write message header: %s", h) } return nil } func (h *messageHeader) read(rd *bufio.Reader) error { h.sessionID = rd.ReadInt64() h.packetCount = rd.ReadInt32() h.varPartLength = rd.ReadUint32() h.varPartSize = rd.ReadUint32() h.noOfSegm = rd.ReadInt16() rd.Skip(10) //messageHeaderSize if trace { outLogger.Printf("read message header: %s", h) } return rd.GetError() } golang-github-sap-go-hdb-0.14.1/internal/protocol/messagetype.go000066400000000000000000000030051346210761100245740ustar00rootroot00000000000000/* 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 ) golang-github-sap-go-hdb-0.14.1/internal/protocol/messagetype_string.go000066400000000000000000000027221346210761100261670ustar00rootroot00000000000000// Code generated by "stringer -type=messageType"; DO NOT EDIT. package protocol import "strconv" 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_1 = [...]uint8{0, 15, 24, 36, 45, 53} _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 "messageType(" + strconv.FormatInt(int64(i), 10) + ")" } } golang-github-sap-go-hdb-0.14.1/internal/protocol/option.go000066400000000000000000000070721346210761100235660ustar00rootroot00000000000000/* 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) { for i := 0; i < lineCnt; i++ { m := plainOptions{} cnt := rd.ReadInt16() m.read(rd, int(cnt)) *o = append(*o, m) } } func (o multiLineOptions) write(wr *bufio.Writer) { for _, m := range o { wr.WriteInt16(int16(len(m))) m.write(wr) } } 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) { for i := 0; i < cnt; i++ { k := rd.ReadInt8() tc := rd.ReadB() switch TypeCode(tc) { default: outLogger.Fatalf("type code %s not implemented", TypeCode(tc)) case tcBoolean: o[k] = booleanType(rd.ReadBool()) case tcInteger: o[k] = intType(rd.ReadInt32()) case tcBigint: o[k] = bigintType(rd.ReadInt64()) case tcDouble: o[k] = doubleType(rd.ReadFloat64()) case tcString: size := rd.ReadInt16() v := make([]byte, size) rd.ReadFull(v) o[k] = stringType(v) case tcBstring: size := rd.ReadInt16() v := make([]byte, size) rd.ReadFull(v) o[k] = binaryStringType(v) } } } func (o plainOptions) write(wr *bufio.Writer) { for k, v := range o { wr.WriteInt8(k) switch v := v.(type) { default: outLogger.Fatalf("type %T not implemented", v) case booleanType: wr.WriteInt8(int8(tcBoolean)) wr.WriteBool(bool(v)) case intType: wr.WriteInt8(int8(tcInteger)) wr.WriteInt32(int32(v)) case bigintType: wr.WriteInt8(int8(tcBigint)) wr.WriteInt64(int64(v)) case doubleType: wr.WriteInt8(int8(tcDouble)) wr.WriteFloat64(float64(v)) case stringType: wr.WriteInt8(int8(tcString)) wr.WriteInt16(int16(len(v))) wr.Write(v) case binaryStringType: wr.WriteInt8(int8(tcBstring)) wr.WriteInt16(int16(len(v))) wr.Write(v) } } } golang-github-sap-go-hdb-0.14.1/internal/protocol/parameter.go000066400000000000000000000215211346210761100242310ustar00rootroot00000000000000/* 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" "io" "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) } // ParameterFieldSet contains database field metadata for parameters. type ParameterFieldSet struct { fields []*ParameterField _inputFields []*ParameterField _outputFields []*ParameterField names fieldNames } func newParameterFieldSet(size int) *ParameterFieldSet { return &ParameterFieldSet{ fields: make([]*ParameterField, size), _inputFields: make([]*ParameterField, 0, size), _outputFields: make([]*ParameterField, 0, size), names: newFieldNames(), } } // String implements the Stringer interface. func (f *ParameterFieldSet) 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 *ParameterFieldSet) read(rd *bufio.Reader) { for i := 0; i < len(f.fields); i++ { field := newParameterField(f.names) field.read(rd) f.fields[i] = field if field.In() { f._inputFields = append(f._inputFields, field) } if field.Out() { f._outputFields = append(f._outputFields, field) } } pos := uint32(0) for _, offset := range f.names.sortOffsets() { if diff := int(offset - pos); diff > 0 { rd.Skip(diff) } b, size := readShortUtf8(rd) f.names.setName(offset, string(b)) pos += uint32(1 + size) } } func (f *ParameterFieldSet) inputFields() []*ParameterField { return f._inputFields } func (f *ParameterFieldSet) outputFields() []*ParameterField { return f._outputFields } // NumInputField returns the number of input fields in a database statement. func (f *ParameterFieldSet) NumInputField() int { return len(f._inputFields) } // NumOutputField returns the number of output fields of a query or stored procedure. func (f *ParameterFieldSet) NumOutputField() int { return len(f._outputFields) } // Field returns the field at index idx. func (f *ParameterFieldSet) Field(idx int) *ParameterField { return f.fields[idx] } // OutputField returns the output field at index idx. func (f *ParameterFieldSet) OutputField(idx int) *ParameterField { return f._outputFields[idx] } // ParameterField contains database field attributes for parameters. type ParameterField struct { fieldNames fieldNames parameterOptions parameterOptions tc TypeCode mode parameterMode fraction int16 length int16 offset uint32 chunkReader lobChunkReader lobLocatorID locatorID } func newParameterField(fieldNames fieldNames) *ParameterField { return &ParameterField{fieldNames: fieldNames} } // String implements the Stringer interface. func (f *ParameterField) String() string { return fmt.Sprintf("parameterOptions %s typeCode %s mode %s fraction %d length %d name %s", f.parameterOptions, f.tc, f.mode, f.fraction, f.length, f.Name(), ) } // TypeCode returns the type code of the field. func (f *ParameterField) TypeCode() TypeCode { return f.tc } // TypeLength returns the type length of the field. // see https://golang.org/pkg/database/sql/driver/#RowsColumnTypeLength func (f *ParameterField) TypeLength() (int64, bool) { if f.tc.isVariableLength() { return int64(f.length), true } return 0, false } // TypePrecisionScale returns the type precision and scale (decimal types) of the field. // see https://golang.org/pkg/database/sql/driver/#RowsColumnTypePrecisionScale func (f *ParameterField) TypePrecisionScale() (int64, int64, bool) { if f.tc.isDecimalType() { return int64(f.length), int64(f.fraction), true } return 0, 0, false } // Nullable returns true if the field may be null, false otherwise. // see https://golang.org/pkg/database/sql/driver/#RowsColumnTypeNullable func (f *ParameterField) Nullable() bool { return f.parameterOptions == poOptional } // In returns true if the parameter field is an input field. func (f *ParameterField) In() bool { return f.mode == pmInout || f.mode == pmIn } // Out returns true if the parameter field is an output field. func (f *ParameterField) Out() bool { return f.mode == pmInout || f.mode == pmOut } // Name returns the parameter field name. func (f *ParameterField) Name() string { return f.fieldNames.name(f.offset) } // SetLobReader sets the io.Reader if a Lob parameter field. func (f *ParameterField) SetLobReader(rd io.Reader) error { f.chunkReader = newLobChunkReader(f.TypeCode().isCharBased(), rd) return nil } // func (f *ParameterField) read(rd *bufio.Reader) { f.parameterOptions = parameterOptions(rd.ReadInt8()) f.tc = TypeCode(rd.ReadInt8()) f.mode = parameterMode(rd.ReadInt8()) rd.Skip(1) //filler f.offset = rd.ReadUint32() f.fieldNames.addOffset(f.offset) f.length = rd.ReadInt16() f.fraction = rd.ReadInt16() rd.Skip(4) //filler } // parameter metadata type parameterMetadata struct { prmFieldSet *ParameterFieldSet numArg int } func (m *parameterMetadata) String() string { return fmt.Sprintf("parameter metadata: %s", m.prmFieldSet.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 { m.prmFieldSet.read(rd) if trace { outLogger.Printf("read %s", m) } return rd.GetError() } // input parameters type inputParameters struct { inputFields []*ParameterField args []driver.NamedValue } func newInputParameters(inputFields []*ParameterField, args []driver.NamedValue) *inputParameters { return &inputParameters{inputFields: inputFields, args: args} } func (p *inputParameters) String() string { return fmt.Sprintf("input parameters: %v", p.args) } func (p *inputParameters) kind() partKind { return pkParameters } func (p *inputParameters) size() (int, error) { size := len(p.args) cnt := len(p.inputFields) for i, arg := range p.args { if arg.Value == nil { // null value continue } // mass insert field := p.inputFields[i%cnt] fieldSize, err := fieldSize(field.TypeCode(), arg) if err != nil { return 0, err } size += fieldSize } return size, nil } func (p *inputParameters) numArg() int { cnt := len(p.inputFields) if cnt == 0 { // avoid divide-by-zero (e.g. prepare without parameters) return 0 } return len(p.args) / cnt } func (p *inputParameters) write(wr *bufio.Writer) error { cnt := len(p.inputFields) for i, arg := range p.args { //mass insert field := p.inputFields[i%cnt] if err := writeField(wr, field.TypeCode(), arg); err != nil { return err } } if trace { outLogger.Printf("input parameters: %s", p) } return nil } // output parameter type outputParameters struct { numArg int s *Session outputFields []*ParameterField fieldValues *FieldValues } func (p *outputParameters) String() string { return fmt.Sprintf("output parameters: %v", p.fieldValues) } func (p *outputParameters) kind() partKind { return pkOutputParameters } func (p *outputParameters) setNumArg(numArg int) { p.numArg = numArg // should always be 1 } func (p *outputParameters) read(rd *bufio.Reader) error { cols := len(p.outputFields) p.fieldValues.resize(p.numArg, cols) for i := 0; i < p.numArg; i++ { for j, field := range p.outputFields { var err error if p.fieldValues.values[i*cols+j], err = readField(p.s, rd, field.TypeCode()); err != nil { return err } } } if trace { outLogger.Printf("read %s", p) } return rd.GetError() } golang-github-sap-go-hdb-0.14.1/internal/protocol/part.go000066400000000000000000000062541346210761100232250ustar00rootroot00000000000000/* 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 { wr.WriteInt8(int8(h.partKind)) wr.WriteInt8(int8(h.partAttributes)) wr.WriteInt16(h.argumentCount) wr.WriteInt32(h.bigArgumentCount) wr.WriteInt32(h.bufferLength) wr.WriteInt32(h.bufferSize) //no filler if trace { outLogger.Printf("write part header: %s", h) } return nil } func (h *partHeader) read(rd *bufio.Reader) error { h.partKind = partKind(rd.ReadInt8()) h.partAttributes = partAttributes(rd.ReadInt8()) h.argumentCount = rd.ReadInt16() h.bigArgumentCount = rd.ReadInt32() h.bufferLength = rd.ReadInt32() h.bufferSize = rd.ReadInt32() // no filler if trace { outLogger.Printf("read part header: %s", h) } return rd.GetError() } golang-github-sap-go-hdb-0.14.1/internal/protocol/partkind.go000066400000000000000000000060261346210761100240700ustar00rootroot00000000000000/* 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 pkClientContext partKind = 29 pkWriteLobReply partKind = 30 pkParameters partKind = 32 pkAuthentication partKind = 33 pkSessionContext partKind = 34 pkClientID partKind = 35 pkProfile partKind = 38 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 pkBatchPrepare partKind = 62 //Reserved: do not use pkBatchExecute partKind = 63 //Reserved: do not use pkTransactionFlags partKind = 64 pkRowSlotImageParamMetadata partKind = 65 //Reserved: do not use pkRowSlotImageResultset partKind = 66 //Reserved: do not use pkDBConnectInfo partKind = 67 pkLobFlags partKind = 68 pkResultsetOptions partKind = 69 pkXATransactionInfo partKind = 70 pkSessionVariable partKind = 71 pkWorkLoadReplayContext partKind = 72 pkSQLReplyOptions partKind = 73 ) golang-github-sap-go-hdb-0.14.1/internal/protocol/partkind_string.go000066400000000000000000000053371346210761100254620ustar00rootroot00000000000000// Code generated by "stringer -type=partKind"; DO NOT EDIT. package protocol import "strconv" const _partKind_name = "pkNilpkCommandpkResultsetpkErrorpkStatementIDpkTransactionIDpkRowsAffectedpkResultsetIDpkTopologyInformationpkTableLocationpkReadLobRequestpkReadLobReplypkAbapIStreampkAbapOStreampkCommandInfopkWriteLobRequestpkClientContextpkWriteLobReplypkParameterspkAuthenticationpkSessionContextpkClientIDpkProfilepkStatementContextpkPartitionInformationpkOutputParameterspkConnectOptionspkCommitOptionspkFetchOptionspkFetchSizepkParameterMetadatapkResultMetadatapkFindLobRequestpkFindLobReplypkItabSHMpkItabChunkMetadatapkItabMetadatapkItabResultChunkpkClientInfopkStreamDatapkOStreamResultpkFDARequestMetadatapkFDAReplyMetadatapkBatchPreparepkBatchExecutepkTransactionFlagspkRowSlotImageParamMetadatapkRowSlotImageResultsetpkDBConnectInfopkLobFlagspkResultsetOptionspkXATransactionInfopkSessionVariablepkWorkLoadReplayContextpkSQLReplyOptions" 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], 29: _partKind_name[209:224], 30: _partKind_name[224:239], 32: _partKind_name[239:251], 33: _partKind_name[251:267], 34: _partKind_name[267:283], 35: _partKind_name[283:293], 38: _partKind_name[293:302], 39: _partKind_name[302:320], 40: _partKind_name[320:342], 41: _partKind_name[342:360], 42: _partKind_name[360:376], 43: _partKind_name[376:391], 44: _partKind_name[391:405], 45: _partKind_name[405:416], 47: _partKind_name[416:435], 48: _partKind_name[435:451], 49: _partKind_name[451:467], 50: _partKind_name[467:481], 51: _partKind_name[481:490], 53: _partKind_name[490:509], 55: _partKind_name[509:523], 56: _partKind_name[523:540], 57: _partKind_name[540:552], 58: _partKind_name[552:564], 59: _partKind_name[564:579], 60: _partKind_name[579:599], 61: _partKind_name[599:617], 62: _partKind_name[617:631], 63: _partKind_name[631:645], 64: _partKind_name[645:663], 65: _partKind_name[663:690], 66: _partKind_name[690:713], 67: _partKind_name[713:728], 68: _partKind_name[728:738], 69: _partKind_name[738:756], 70: _partKind_name[756:775], 71: _partKind_name[775:792], 72: _partKind_name[792:815], 73: _partKind_name[815:832], } func (i partKind) String() string { if str, ok := _partKind_map[i]; ok { return str } return "partKind(" + strconv.FormatInt(int64(i), 10) + ")" } golang-github-sap-go-hdb-0.14.1/internal/protocol/querytype.go000066400000000000000000000014371346210761100243240ustar00rootroot00000000000000/* 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 ) golang-github-sap-go-hdb-0.14.1/internal/protocol/querytype_string.go000066400000000000000000000006461346210761100257130ustar00rootroot00000000000000// Code generated by "stringer -type=QueryType"; DO NOT EDIT. package protocol import "strconv" 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 "QueryType(" + strconv.FormatInt(int64(i), 10) + ")" } return _QueryType_name[_QueryType_index[i]:_QueryType_index[i+1]] } golang-github-sap-go-hdb-0.14.1/internal/protocol/result.go000066400000000000000000000145431346210761100235750ustar00rootroot00000000000000/* 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 := rd.ReadUint64() *id.id = _id if trace { outLogger.Printf("resultset id: %d", *id.id) } return rd.GetError() } func (id *resultsetID) write(wr *bufio.Writer) error { wr.WriteUint64(*id.id) if trace { outLogger.Printf("resultset id: %d", *id.id) } return nil } // ResultFieldSet contains database field metadata for result fields. type ResultFieldSet struct { fields []*ResultField names fieldNames } func newResultFieldSet(size int) *ResultFieldSet { return &ResultFieldSet{ fields: make([]*ResultField, size), names: newFieldNames(), } } // String implements the Stringer interface. func (f *ResultFieldSet) 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 *ResultFieldSet) read(rd *bufio.Reader) { for i := 0; i < len(f.fields); i++ { field := newResultField(f.names) field.read(rd) f.fields[i] = field } pos := uint32(0) for _, offset := range f.names.sortOffsets() { diff := int(offset - pos) if diff > 0 { rd.Skip(diff) } b, size := readShortUtf8(rd) f.names.setName(offset, string(b)) pos += uint32(1 + size + diff) } } // NumField returns the number of fields of a query. func (f *ResultFieldSet) NumField() int { return len(f.fields) } // Field returns the field at index idx. func (f *ResultFieldSet) Field(idx int) *ResultField { return f.fields[idx] } const ( tableName = iota schemaName columnName columnDisplayName maxNames ) // ResultField contains database field attributes for result fields. type ResultField struct { fieldNames fieldNames columnOptions columnOptions tc TypeCode fraction int16 length int16 offsets [maxNames]uint32 } func newResultField(fieldNames fieldNames) *ResultField { return &ResultField{fieldNames: fieldNames} } // String implements the Stringer interface. func (f *ResultField) String() string { return fmt.Sprintf("columnsOptions %s typeCode %s fraction %d length %d tablename %s schemaname %s columnname %s columnDisplayname %s", f.columnOptions, f.tc, f.fraction, f.length, f.fieldNames.name(f.offsets[tableName]), f.fieldNames.name(f.offsets[schemaName]), f.fieldNames.name(f.offsets[columnName]), f.fieldNames.name(f.offsets[columnDisplayName]), ) } // TypeCode returns the type code of the field. func (f *ResultField) TypeCode() TypeCode { return f.tc } // TypeLength returns the type length of the field. // see https://golang.org/pkg/database/sql/driver/#RowsColumnTypeLength func (f *ResultField) TypeLength() (int64, bool) { if f.tc.isVariableLength() { return int64(f.length), true } return 0, false } // TypePrecisionScale returns the type precision and scale (decimal types) of the field. // see https://golang.org/pkg/database/sql/driver/#RowsColumnTypePrecisionScale func (f *ResultField) TypePrecisionScale() (int64, int64, bool) { if f.tc.isDecimalType() { return int64(f.length), int64(f.fraction), true } return 0, 0, false } // Nullable returns true if the field may be null, false otherwise. // see https://golang.org/pkg/database/sql/driver/#RowsColumnTypeNullable func (f *ResultField) Nullable() bool { return f.columnOptions == coOptional } // Name returns the result field name. func (f *ResultField) Name() string { return f.fieldNames.name(f.offsets[columnDisplayName]) } func (f *ResultField) read(rd *bufio.Reader) { f.columnOptions = columnOptions(rd.ReadInt8()) f.tc = TypeCode(rd.ReadInt8()) f.fraction = rd.ReadInt16() f.length = rd.ReadInt16() rd.Skip(2) //filler for i := 0; i < maxNames; i++ { offset := rd.ReadUint32() f.offsets[i] = offset f.fieldNames.addOffset(offset) } } //resultset metadata type resultMetadata struct { resultFieldSet *ResultFieldSet numArg int } func (r *resultMetadata) String() string { return fmt.Sprintf("result metadata: %s", r.resultFieldSet.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 { r.resultFieldSet.read(rd) if trace { outLogger.Printf("read %s", r) } return rd.GetError() } //resultset type resultset struct { numArg int s *Session resultFieldSet *ResultFieldSet 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 { cols := len(r.resultFieldSet.fields) r.fieldValues.resize(r.numArg, cols) for i := 0; i < r.numArg; i++ { for j, field := range r.resultFieldSet.fields { var err error if r.fieldValues.values[i*cols+j], err = readField(r.s, rd, field.TypeCode()); err != nil { return err } } } if trace { outLogger.Printf("read %s", r) } return rd.GetError() } golang-github-sap-go-hdb-0.14.1/internal/protocol/rowsaffected.go000066400000000000000000000026471346210761100247350ustar00rootroot00000000000000/* 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 { rows []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.rows == nil || r._numArg > cap(r.rows) { r.rows = make([]int32, r._numArg) } else { r.rows = r.rows[:r._numArg] } for i := 0; i < r._numArg; i++ { r.rows[i] = rd.ReadInt32() if trace { outLogger.Printf("rows affected %d: %d", i, r.rows[i]) } } return rd.GetError() } func (r *rowsAffected) total() int64 { if r.rows == nil { return 0 } total := int64(0) for _, rows := range r.rows { if rows > 0 { total += int64(rows) } } return total } golang-github-sap-go-hdb-0.14.1/internal/protocol/scramsha256.go000066400000000000000000000136221346210761100243120ustar00rootroot00000000000000/* 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 (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 { wr.WriteInt16(3) writeAuthField(wr, r.username) writeAuthField(wr, []byte(mnSCRAMSHA256)) writeAuthField(wr, r.clientChallenge) return nil } type scramsha256InitialReply struct { salt []byte serverChallenge []byte } func (r *scramsha256InitialReply) kind() partKind { return pkAuthentication } func (r *scramsha256InitialReply) setNumArg(int) { //not needed } func (r *scramsha256InitialReply) read(rd *bufio.Reader) error { cnt := rd.ReadInt16() if err := readMethodName(rd); err != nil { return err } size := rd.ReadB() if size != serverChallengeDataSize { return fmt.Errorf("invalid server challenge data size %d - %d expected", size, serverChallengeDataSize) } //server challenge data cnt = rd.ReadInt16() if cnt != 2 { return fmt.Errorf("invalid server challenge data field count %d - %d expected", cnt, 2) } size = rd.ReadB() if trace { outLogger.Printf("salt size %d", size) } r.salt = make([]byte, size) rd.ReadFull(r.salt) if trace { outLogger.Printf("salt %v", r.salt) } size = rd.ReadB() r.serverChallenge = make([]byte, size) rd.ReadFull(r.serverChallenge) if trace { outLogger.Printf("server challenge %v", r.serverChallenge) } return rd.GetError() } 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 { wr.WriteInt16(3) writeAuthField(wr, r.username) writeAuthField(wr, []byte(mnSCRAMSHA256)) writeAuthField(wr, r.clientProof) 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 := rd.ReadInt16() 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 := rd.ReadB() serverProof := make([]byte, size) rd.ReadFull(serverProof) return rd.GetError() } //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) { 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") } wr.WriteB(byte(size)) wr.Write(f) } func readMethodName(rd *bufio.Reader) error { size := rd.ReadB() methodName := make([]byte, size) rd.ReadFull(methodName) 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 } golang-github-sap-go-hdb-0.14.1/internal/protocol/scramsha256_test.go000066400000000000000000000037541346210761100253560ustar00rootroot00000000000000/* 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 ( "testing" ) type testRecord struct { salt []byte serverChallenge []byte clientChallenge []byte password []byte clientProof []byte } var testData = []*testRecord{ &testRecord{ salt: []byte{214, 199, 255, 118, 92, 174, 94, 190, 197, 225, 57, 154, 157, 109, 119, 245}, serverChallenge: []byte{224, 22, 242, 18, 237, 99, 6, 28, 162, 248, 96, 7, 115, 152, 134, 65, 141, 65, 168, 126, 168, 86, 87, 72, 16, 119, 12, 91, 227, 123, 51, 194, 203, 168, 56, 133, 70, 236, 230, 214, 89, 167, 130, 123, 132, 178, 211, 186}, clientChallenge: []byte{219, 141, 27, 200, 255, 90, 182, 125, 133, 151, 127, 36, 26, 106, 213, 31, 57, 89, 50, 201, 237, 11, 158, 110, 8, 13, 2, 71, 9, 235, 213, 27, 64, 43, 181, 181, 147, 140, 10, 63, 156, 133, 133, 165, 171, 67, 187, 250, 41, 145, 176, 164, 137, 54, 72, 42, 47, 112, 252, 77, 102, 152, 220, 223}, password: []byte{65, 100, 109, 105, 110, 49, 50, 51, 52}, clientProof: []byte{0, 1, 32, 23, 243, 209, 70, 117, 54, 25, 92, 21, 173, 194, 108, 63, 25, 188, 185, 230, 61, 124, 190, 73, 80, 225, 126, 191, 119, 32, 112, 231, 72, 184, 199}, }, } func TestScramsha256(t *testing.T) { for _, r := range testData { clientProof := clientProof(r.salt, r.serverChallenge, r.clientChallenge, r.password) for i, v := range clientProof { if v != r.clientProof[i] { t.Fatalf("diff index % d - got %v - expected %v", i, clientProof, r.clientProof) } } } } golang-github-sap-go-hdb-0.14.1/internal/protocol/segment.go000066400000000000000000000076551346210761100237270ustar00rootroot00000000000000/* 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 { wr.WriteInt32(h.segmentLength) wr.WriteInt32(h.segmentOfs) wr.WriteInt16(h.noOfParts) wr.WriteInt16(h.segmentNo) wr.WriteInt8(int8(h.segmentKind)) switch h.segmentKind { default: //error wr.WriteZeroes(11) //segmentHeaderLength case skRequest: wr.WriteInt8(int8(h.messageType)) wr.WriteBool(h.commit) wr.WriteInt8(int8(h.commandOptions)) wr.WriteZeroes(8) //segmentHeaderSize case skReply: wr.WriteZeroes(1) //reserved wr.WriteInt16(int16(h.functionCode)) wr.WriteZeroes(8) //segmentHeaderSize } if trace { outLogger.Printf("write segment header: %s", h) } return nil } // reply || error func (h *segmentHeader) read(rd *bufio.Reader) error { h.segmentLength = rd.ReadInt32() h.segmentOfs = rd.ReadInt32() h.noOfParts = rd.ReadInt16() h.segmentNo = rd.ReadInt16() h.segmentKind = segmentKind(rd.ReadInt8()) switch h.segmentKind { default: //error rd.Skip(11) //segmentHeaderLength case skRequest: h.messageType = messageType(rd.ReadInt8()) h.commit = rd.ReadBool() h.commandOptions = commandOptions(rd.ReadInt8()) rd.Skip(8) //segmentHeaderLength case skReply: rd.Skip(1) //reserved h.functionCode = functionCode(rd.ReadInt16()) rd.Skip(8) //segmentHeaderLength } if trace { outLogger.Printf("read segment header: %s", h) } return rd.GetError() } golang-github-sap-go-hdb-0.14.1/internal/protocol/segmentkind.go000066400000000000000000000013631346210761100245630ustar00rootroot00000000000000/* 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 ) golang-github-sap-go-hdb-0.14.1/internal/protocol/segmentkind_string.go000066400000000000000000000010211346210761100261400ustar00rootroot00000000000000// Code generated by "stringer -type=segmentKind"; DO NOT EDIT. package protocol import "strconv" const ( _segmentKind_name_0 = "skInvalidskRequestskReply" _segmentKind_name_1 = "skError" ) var ( _segmentKind_index_0 = [...]uint8{0, 9, 18, 25} ) 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 "segmentKind(" + strconv.FormatInt(int64(i), 10) + ")" } } golang-github-sap-go-hdb-0.14.1/internal/protocol/session.go000066400000000000000000000554701346210761100237460ustar00rootroot00000000000000/* 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 ( "context" "crypto/tls" "database/sql/driver" "flag" "fmt" "log" "math" "net" "os" "sync" "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 timeout time.Duration conn net.Conn isBad bool // bad connection badError error // error cause for session bad state inTx bool // in transaction } func newSessionConn(ctx context.Context, addr string, timeoutSec int, config *tls.Config) (*sessionConn, error) { timeout := time.Duration(timeoutSec) * time.Second dialer := net.Dialer{Timeout: timeout} conn, err := dialer.DialContext(ctx, "tcp", addr) if err != nil { return nil, err } // is TLS connection requested? if config != nil { conn = tls.Client(conn, config) } return &sessionConn{addr: addr, timeout: timeout, 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.timeout)); 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.timeout)); 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 beforeRead func(p replyPart) // session parameter type sessionPrm interface { Host() string Username() string Password() string Locale() string FetchSize() int Timeout() int TLSConfig() *tls.Config } // 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 scramsha256InitialRequest *scramsha256InitialRequest scramsha256InitialReply *scramsha256InitialReply scramsha256FinalRequest *scramsha256FinalRequest scramsha256FinalReply *scramsha256FinalReply topologyInformation *topologyInformation connectOptions *connectOptions rowsAffected *rowsAffected statementID *statementID resultMetadata *resultMetadata resultsetID *resultsetID resultset *resultset parameterMetadata *parameterMetadata outputParameters *outputParameters writeLobRequest *writeLobRequest readLobRequest *readLobRequest writeLobReply *writeLobReply readLobReply *readLobReply //standard replies stmtCtx *statementContext txFlags *transactionFlags lastError *hdbErrors //serialize write request - read reply //supports calling session methods in go routines (driver methods with context cancellation) mu sync.Mutex } // NewSession creates a new database session. func NewSession(ctx context.Context, prm sessionPrm) (*Session, error) { if trace { outLogger.Printf("%s", prm) } conn, err := newSessionConn(ctx, prm.Host(), prm.Timeout(), prm.TLSConfig()) if err != nil { return nil, err } 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), scramsha256InitialRequest: new(scramsha256InitialRequest), scramsha256InitialReply: new(scramsha256InitialReply), scramsha256FinalRequest: new(scramsha256FinalRequest), scramsha256FinalReply: new(scramsha256FinalReply), topologyInformation: newTopologyInformation(), connectOptions: newConnectOptions(), rowsAffected: new(rowsAffected), statementID: new(statementID), resultMetadata: new(resultMetadata), resultsetID: new(resultsetID), resultset: new(resultset), parameterMetadata: new(parameterMetadata), outputParameters: new(outputParameters), writeLobRequest: new(writeLobRequest), readLobRequest: new(readLobRequest), writeLobReply: new(writeLobReply), readLobReply: new(readLobReply), stmtCtx: newStatementContext(), txFlags: newTransactionFlags(), lastError: new(hdbErrors), } 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 s.scramsha256InitialRequest.username = username s.scramsha256InitialRequest.clientChallenge = clientChallenge if err := s.writeRequest(mtAuthenticate, false, s.scramsha256InitialRequest); err != nil { return err } if err := s.readReply(nil); err != nil { return err } //final request s.scramsha256FinalRequest.username = username s.scramsha256FinalRequest.clientProof = clientProof(s.scramsha256InitialReply.salt, s.scramsha256InitialReply.serverChallenge, clientChallenge, password) s.scramsha256InitialReply = nil // !!! next time readReply uses FinalReply id := newClientID() co := newConnectOptions() co.set(coDistributionProtocolVersion, booleanType(false)) co.set(coSelectForUpdateSupported, booleanType(false)) co.set(coSplitBatchCommands, booleanType(true)) // cannot use due to HDB protocol error with secondtime datatype //co.set(coDataFormatVersion2, dfvSPS06) co.set(coDataFormatVersion2, dfvBaseline) co.set(coCompleteArrayExecution, booleanType(true)) if s.prm.Locale() != "" { co.set(coClientLocale, stringType(s.prm.Locale())) } co.set(coClientDistributionMode, cdmOff) // setting this option has no effect //co.set(coImplicitLobStreaming, booleanType(true)) if err := s.writeRequest(mtConnect, false, s.scramsha256FinalRequest, id, co); err != nil { return err } if err := s.readReply(nil); err != nil { return err } return nil } // QueryDirect executes a query without query parameters. func (s *Session) QueryDirect(query string) (uint64, *ResultFieldSet, *FieldValues, PartAttributes, error) { s.mu.Lock() defer s.mu.Unlock() if err := s.writeRequest(mtExecuteDirect, false, command(query)); err != nil { return 0, nil, nil, nil, err } var id uint64 var resultFieldSet *ResultFieldSet fieldValues := newFieldValues() f := func(p replyPart) { switch p := p.(type) { case *resultsetID: p.id = &id case *resultMetadata: resultFieldSet = newResultFieldSet(p.numArg) p.resultFieldSet = resultFieldSet case *resultset: p.s = s p.resultFieldSet = resultFieldSet p.fieldValues = fieldValues } } if err := s.readReply(f); err != nil { return 0, nil, nil, nil, err } return id, resultFieldSet, fieldValues, s.ph.partAttributes, nil } // ExecDirect executes a sql statement without statement parameters. func (s *Session) ExecDirect(query string) (driver.Result, error) { s.mu.Lock() defer s.mu.Unlock() if err := s.writeRequest(mtExecuteDirect, !s.conn.inTx, command(query)); err != nil { return nil, err } if err := s.readReply(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, *ParameterFieldSet, *ResultFieldSet, error) { s.mu.Lock() defer s.mu.Unlock() if err := s.writeRequest(mtPrepare, false, command(query)); err != nil { return QtNone, 0, nil, nil, err } var id uint64 var prmFieldSet *ParameterFieldSet var resultFieldSet *ResultFieldSet f := func(p replyPart) { switch p := p.(type) { case *statementID: p.id = &id case *parameterMetadata: prmFieldSet = newParameterFieldSet(p.numArg) p.prmFieldSet = prmFieldSet case *resultMetadata: resultFieldSet = newResultFieldSet(p.numArg) p.resultFieldSet = resultFieldSet } } if err := s.readReply(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, prmFieldSet *ParameterFieldSet, args []driver.NamedValue) (driver.Result, error) { s.mu.Lock() defer s.mu.Unlock() s.statementID.id = &id if err := s.writeRequest(mtExecute, !s.conn.inTx, s.statementID, newInputParameters(prmFieldSet.inputFields(), args)); err != nil { return nil, err } if err := s.readReply(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 err := s.writeLobStream(prmFieldSet, nil, args); err != nil { return nil, err } return result, nil } // DropStatementID releases the hdb statement handle. func (s *Session) DropStatementID(id uint64) error { s.mu.Lock() defer s.mu.Unlock() s.statementID.id = &id if err := s.writeRequest(mtDropStatementID, false, s.statementID); err != nil { return err } if err := s.readReply(nil); err != nil { return err } return nil } // Call executes a stored procedure. func (s *Session) Call(id uint64, prmFieldSet *ParameterFieldSet, args []driver.NamedValue) (*FieldValues, []*TableResult, error) { s.mu.Lock() defer s.mu.Unlock() s.statementID.id = &id if err := s.writeRequest(mtExecute, false, s.statementID, newInputParameters(prmFieldSet.inputFields(), args)); err != nil { return nil, nil, err } prmFieldValues := newFieldValues() var tableResults []*TableResult var tableResult *TableResult f := func(p replyPart) { switch p := p.(type) { case *outputParameters: p.s = s p.outputFields = prmFieldSet.outputFields() p.fieldValues = prmFieldValues // table output parameters: meta, id, result (only first param?) case *resultMetadata: tableResult = newTableResult(s, p.numArg) tableResults = append(tableResults, tableResult) p.resultFieldSet = tableResult.resultFieldSet case *resultsetID: p.id = &(tableResult.id) case *resultset: p.s = s tableResult.attrs = s.ph.partAttributes p.resultFieldSet = tableResult.resultFieldSet p.fieldValues = tableResult.fieldValues } } if err := s.readReply(f); err != nil { return nil, nil, err } if err := s.writeLobStream(prmFieldSet, prmFieldValues, args); err != nil { return nil, nil, err } return prmFieldValues, tableResults, nil } // Query executes a query. func (s *Session) Query(stmtID uint64, prmFieldSet *ParameterFieldSet, resultFieldSet *ResultFieldSet, args []driver.NamedValue) (uint64, *FieldValues, PartAttributes, error) { s.mu.Lock() defer s.mu.Unlock() s.statementID.id = &stmtID if err := s.writeRequest(mtExecute, false, s.statementID, newInputParameters(prmFieldSet.inputFields(), args)); err != nil { return 0, nil, nil, err } var rsetID uint64 fieldValues := newFieldValues() f := func(p replyPart) { switch p := p.(type) { case *resultsetID: p.id = &rsetID case *resultset: p.s = s p.resultFieldSet = resultFieldSet p.fieldValues = fieldValues } } if err := s.readReply(f); err != nil { return 0, nil, nil, err } return rsetID, fieldValues, s.ph.partAttributes, nil } // FetchNext fetches next chunk in query result set. func (s *Session) FetchNext(id uint64, resultFieldSet *ResultFieldSet, fieldValues *FieldValues) (PartAttributes, error) { s.mu.Lock() defer s.mu.Unlock() s.resultsetID.id = &id if err := s.writeRequest(mtFetchNext, false, s.resultsetID, fetchsize(s.prm.FetchSize())); err != nil { return nil, err } f := func(p replyPart) { switch p := p.(type) { case *resultset: p.s = s p.resultFieldSet = resultFieldSet p.fieldValues = fieldValues } } if err := s.readReply(f); err != nil { return nil, err } return s.ph.partAttributes, nil } // CloseResultsetID releases the hdb resultset handle. func (s *Session) CloseResultsetID(id uint64) error { s.mu.Lock() defer s.mu.Unlock() s.resultsetID.id = &id if err := s.writeRequest(mtCloseResultset, false, s.resultsetID); err != nil { return err } if err := s.readReply(nil); err != nil { return err } return nil } // Commit executes a database commit. func (s *Session) Commit() error { s.mu.Lock() defer s.mu.Unlock() if err := s.writeRequest(mtCommit, false); err != nil { return err } if err := s.readReply(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 { s.mu.Lock() defer s.mu.Unlock() if err := s.writeRequest(mtRollback, false); err != nil { return err } if err := s.readReply(nil); err != nil { return err } if trace { outLogger.Printf("transaction flags: %s", s.txFlags) } s.conn.inTx = false return nil } // func (s *Session) readLobStream(w lobChunkWriter) error { s.readLobRequest.w = w s.readLobReply.w = w for !w.eof() { if err := s.writeRequest(mtWriteLob, false, s.readLobRequest); err != nil { return err } if err := s.readReply(nil); err != nil { return err } } return nil } func (s *Session) writeLobStream(prmFieldSet *ParameterFieldSet, prmFieldValues *FieldValues, args []driver.NamedValue) error { if s.writeLobReply.numArg == 0 { return nil } lobPrmFields := make([]*ParameterField, s.writeLobReply.numArg) j := 0 for _, f := range prmFieldSet.fields { if f.TypeCode().isLob() && f.In() && f.chunkReader != nil { f.lobLocatorID = s.writeLobReply.ids[j] lobPrmFields[j] = f j++ } } if j != s.writeLobReply.numArg { return fmt.Errorf("protocol error: invalid number of lob parameter ids %d - expected %d", j, s.writeLobReply.numArg) } s.writeLobRequest.lobPrmFields = lobPrmFields f := func(p replyPart) { if p, ok := p.(*outputParameters); ok { p.s = s p.outputFields = prmFieldSet.outputFields() p.fieldValues = prmFieldValues } } for s.writeLobReply.numArg != 0 { if err := s.writeRequest(mtReadLob, false, s.writeLobRequest); err != nil { return err } if err := s.readReply(f); 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 = littleEndian 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 } s.wr.WriteZeroes(pad) bufferSize -= int64(partHeaderSize + size + pad) } return s.wr.Flush() } func (s *Session) readReply(beforeRead beforeRead) error { replyRowsAffected := false 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) lastPart := noOfParts - 1 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 switch s.ph.partKind { case pkAuthentication: if s.scramsha256InitialReply != nil { // first call: initial reply part = s.scramsha256InitialReply } else { // second call: final reply part = s.scramsha256FinalReply } case pkTopologyInformation: part = s.topologyInformation case pkConnectOptions: part = s.connectOptions 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: replyRowsAffected = true part = s.rowsAffected case pkReadLobReply: part = s.readLobReply case pkWriteLobReply: part = s.writeLobReply default: return fmt.Errorf("read not expected part kind %s", s.ph.partKind) } part.setNumArg(numArg) if beforeRead != nil { beforeRead(part) } s.rd.ResetCnt() if err := part.read(s.rd); err != nil { return err } cnt := s.rd.Cnt() switch { case cnt < int(s.ph.bufferLength): // protocol buffer length > read bytes -> skip the unread bytes s.rd.Skip(int(s.ph.bufferLength) - cnt) case cnt > int(s.ph.bufferLength): // read bytes > protocol buffer length -> should never happen return fmt.Errorf("protocol error: read bytes %d > buffer length %d", cnt, s.ph.bufferLength) } if i != lastPart { // not last part s.rd.Skip(padBytes(int(s.ph.bufferLength))) } } // last part // TODO: workaround (see *) if diff == 0 { s.rd.Skip(padBytes(int(s.ph.bufferLength))) } if err := s.rd.GetError(); err != nil { return err } if replyError { if replyRowsAffected { //link statement to error j := 0 for i, rows := range s.rowsAffected.rows { if rows == raExecutionFailed { s.lastError.setStmtNo(j, i) j++ } } } if s.lastError.isWarnings() { for _, _error := range s.lastError.errors { sqltrace.Traceln(_error) } return nil } return s.lastError } return nil } golang-github-sap-go-hdb-0.14.1/internal/protocol/sniffer.go000066400000000000000000000102121346210761100237000ustar00rootroot00000000000000/* 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 } } } return to.Flush() } 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) } from.ReadFull(b) err := from.GetError() if 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]) } to.Write(b) 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 } golang-github-sap-go-hdb-0.14.1/internal/protocol/statementcontext.go000066400000000000000000000025011346210761100256570ustar00rootroot00000000000000/* 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 { c.options.read(rd, c._numArg) if trace { outLogger.Printf("statement context: %v", c) } return rd.GetError() } golang-github-sap-go-hdb-0.14.1/internal/protocol/statementcontexttype.go000066400000000000000000000013751346210761100265710ustar00rootroot00000000000000/* 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 ) golang-github-sap-go-hdb-0.14.1/internal/protocol/statementcontexttype_string.go000066400000000000000000000010631346210761100301510ustar00rootroot00000000000000// Code generated by "stringer -type=statementContextType"; DO NOT EDIT. package protocol import "strconv" 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 "statementContextType(" + strconv.FormatInt(int64(i+1), 10) + ")" } return _statementContextType_name[_statementContextType_index[i]:_statementContextType_index[i+1]] } golang-github-sap-go-hdb-0.14.1/internal/protocol/statementid.go000066400000000000000000000024101346210761100245660ustar00rootroot00000000000000/* 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 := rd.ReadUint64() *id.id = _id if trace { outLogger.Printf("statement id: %d", *id.id) } return rd.GetError() } func (id statementID) write(wr *bufio.Writer) error { wr.WriteUint64(*id.id) if trace { outLogger.Printf("statement id: %d", *id.id) } return nil } golang-github-sap-go-hdb-0.14.1/internal/protocol/tableresult.go000066400000000000000000000027371346210761100246070ustar00rootroot00000000000000/* 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 resultFieldSet *ResultFieldSet fieldValues *FieldValues attrs partAttributes } func newTableResult(s *Session, size int) *TableResult { return &TableResult{ resultFieldSet: newResultFieldSet(size), fieldValues: newFieldValues(), } } // 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() *ResultFieldSet { return r.resultFieldSet } // 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 } golang-github-sap-go-hdb-0.14.1/internal/protocol/time.go000066400000000000000000000034521346210761100232120ustar00rootroot00000000000000/* 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 protocol import ( "time" ) const gregorianDay = 2299161 // Start date of Gregorian Calendar as Julian Day Number var gregorianDate = julianDayToTime(gregorianDay) // Start date of Gregorian Calendar (1582-10-15) // timeToJulianDay returns the Julian Date Number of time's date components. // The algorithm is taken from https://en.wikipedia.org/wiki/Julian_day. func timeToJulianDay(t time.Time) int { t = t.UTC() month := int(t.Month()) a := (14 - month) / 12 y := t.Year() + 4800 - a m := month + (12 * a) - 3 if t.Before(gregorianDate) { // Julian Calendar return t.Day() + (153*m+2)/5 + 365*y + y/4 - 32083 } // Gregorian Calendar return t.Day() + (153*m+2)/5 + 365*y + y/4 - y/100 + y/400 - 32045 } // JulianDayToTime returns the correcponding UTC date for a Julian Day Number. // The algorithm is taken from https://en.wikipedia.org/wiki/Julian_day. func julianDayToTime(jd int) time.Time { var f int if jd < gregorianDay { f = jd + 1401 } else { f = jd + 1401 + (((4*jd+274277)/146097)*3)/4 - 38 } e := 4*f + 3 g := (e % 1461) / 4 h := 5*g + 2 day := (h%153)/5 + 1 month := (h/153+2)%12 + 1 year := (e / 1461) - 4716 + (12+2-month)/12 return time.Date(year, time.Month(month), day, 0, 0, 0, 0, time.UTC) } golang-github-sap-go-hdb-0.14.1/internal/protocol/time_test.go000066400000000000000000000044031346210761100242460ustar00rootroot00000000000000/* 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 protocol import ( "testing" "time" ) type testJulianDay struct { jd int time time.Time } var testJulianDayData = []testJulianDay{ testJulianDay{1721424, time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC)}, testJulianDay{1842713, time.Date(333, time.January, 27, 0, 0, 0, 0, time.UTC)}, testJulianDay{2299160, time.Date(1582, time.October, 4, 0, 0, 0, 0, time.UTC)}, testJulianDay{2299161, time.Date(1582, time.October, 15, 0, 0, 0, 0, time.UTC)}, testJulianDay{2415021, time.Date(1900, time.January, 1, 0, 0, 0, 0, time.UTC)}, testJulianDay{2447893, time.Date(1990, time.January, 1, 0, 0, 0, 0, time.UTC)}, testJulianDay{2451545, time.Date(2000, time.January, 1, 0, 0, 0, 0, time.UTC)}, testJulianDay{2453750, time.Date(2006, time.January, 14, 0, 0, 0, 0, time.UTC)}, testJulianDay{2455281, time.Date(2010, time.March, 25, 0, 0, 0, 0, time.UTC)}, testJulianDay{2457188, time.Date(2015, time.June, 14, 0, 0, 0, 0, time.UTC)}, testJulianDay{2440587, time.Date(1969, time.December, 31, 0, 0, 0, 0, time.UTC)}, testJulianDay{2440588, time.Date(1970, time.January, 1, 0, 0, 0, 0, time.UTC)}, testJulianDay{5373484, time.Date(9999, time.December, 31, 0, 0, 0, 0, time.UTC)}, testJulianDay{2457202, time.Date(2015, time.June, 28, 0, 0, 0, 0, time.UTC)}, } func TestTimeToJulianDay(t *testing.T) { for i, d := range testJulianDayData { jd := timeToJulianDay(d.time) if jd != d.jd { t.Fatalf("Julian Day Number %d value %d - expected %d (date %s)", i, jd, d.jd, d.time) } } } func TestJulianDayToTime(t *testing.T) { for i, d := range testJulianDayData { time := julianDayToTime(d.jd) if !time.Equal(d.time) { t.Fatalf("Time %d value %s - expected %s (Julian Day Number %d)", i, time, d.time, d.jd) } } } golang-github-sap-go-hdb-0.14.1/internal/protocol/topology.go000066400000000000000000000034261346210761100241310ustar00rootroot00000000000000/* 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 topologyInformation struct { mlo multiLineOptions _numArg int } func newTopologyInformation() *topologyInformation { return &topologyInformation{ mlo: multiLineOptions{}, } } func (o *topologyInformation) 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 *topologyInformation) kind() partKind { return pkTopologyInformation } func (o *topologyInformation) size() int { return o.mlo.size() } func (o *topologyInformation) numArg() int { return len(o.mlo) } func (o *topologyInformation) setNumArg(numArg int) { o._numArg = numArg } func (o *topologyInformation) read(rd *bufio.Reader) error { o.mlo.read(rd, o._numArg) if trace { outLogger.Printf("topology options: %v", o) } return rd.GetError() } func (o *topologyInformation) write(wr *bufio.Writer) error { for _, m := range o.mlo { wr.WriteInt16(int16(len(m))) o.mlo.write(wr) } if trace { outLogger.Printf("topology options: %v", o) } return nil } golang-github-sap-go-hdb-0.14.1/internal/protocol/topologyoption.go000066400000000000000000000021441346210761100253560ustar00rootroot00000000000000/* 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 ) golang-github-sap-go-hdb-0.14.1/internal/protocol/topologyoption_string.go000066400000000000000000000012251346210761100267430ustar00rootroot00000000000000// Code generated by "stringer -type=topologyOption"; DO NOT EDIT. package protocol import "strconv" 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 "topologyOption(" + strconv.FormatInt(int64(i+1), 10) + ")" } return _topologyOption_name[_topologyOption_index[i]:_topologyOption_index[i+1]] } golang-github-sap-go-hdb-0.14.1/internal/protocol/transactionflags.go000066400000000000000000000024771346210761100256240ustar00rootroot00000000000000/* 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 { f.options.read(rd, f._numArg) if trace { outLogger.Printf("transaction flags: %v", f) } return rd.GetError() } golang-github-sap-go-hdb-0.14.1/internal/protocol/transactionflagtype.go000066400000000000000000000021011346210761100263230ustar00rootroot00000000000000/* 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 ) golang-github-sap-go-hdb-0.14.1/internal/protocol/transactionflagtype_string.go000066400000000000000000000012341346210761100277170ustar00rootroot00000000000000// Code generated by "stringer -type=transactionFlagType"; DO NOT EDIT. package protocol import "strconv" 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 "transactionFlagType(" + strconv.FormatInt(int64(i), 10) + ")" } return _transactionFlagType_name[_transactionFlagType_index[i]:_transactionFlagType_index[i+1]] } golang-github-sap-go-hdb-0.14.1/internal/protocol/typecode.go000066400000000000000000000124231346210761100240660ustar00rootroot00000000000000/* 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 ( "strings" ) //go:generate stringer -type=TypeCode // TypeCode identify the type of a field transferred to or from the database. type TypeCode byte // null value indicator is high bit const ( tcNull TypeCode = 0 tcTinyint TypeCode = 1 tcSmallint TypeCode = 2 tcInteger 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 // deprecated with 3 (doku) - but table 'date' field uses it tcDate TypeCode = 14 // deprecated with 3 (doku) - but table 'time' field uses it tcTime TypeCode = 15 // deprecated 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 tcText TypeCode = 51 tcShorttext TypeCode = 52 //tcFixedString TypeCode = 53 // reserved: do not use //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 //tcCte TypeCode = 65 // reserved: do not use //tcCstimesda 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 // reserved: do not use //tcPoint TypeCode = 75 // reserved: do not use //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 // reserved: do not use ) 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) isVariableLength() bool { return k == tcChar || k == tcNchar || k == tcVarchar || k == tcNvarchar || k == tcBinary || k == tcVarbinary || k == tcShorttext || k == tcAlphanum } func (k TypeCode) isDecimalType() bool { return k == tcSmalldecimal || k == tcDecimal } // DataType converts a type code into one of the supported data types by the driver. func (k TypeCode) DataType() DataType { switch k { default: return DtUnknown case tcTinyint: return DtTinyint case tcSmallint: return DtSmallint case tcInteger: return DtInteger case tcBigint: return DtBigint case tcReal: return DtReal case tcDouble: return DtDouble case tcDate, tcTime, tcTimestamp, tcLongdate, tcSeconddate, tcDaydate, tcSecondtime: 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 } } // TypeName returns the database type name. // see https://golang.org/pkg/database/sql/driver/#RowsColumnTypeDatabaseTypeName func (k TypeCode) TypeName() string { return strings.ToUpper(k.String()[2:]) } golang-github-sap-go-hdb-0.14.1/internal/protocol/typecode_string.go000066400000000000000000000032171346210761100254550ustar00rootroot00000000000000// Code generated by "stringer -type=TypeCode"; DO NOT EDIT. package protocol import "strconv" const ( _TypeCode_name_0 = "tcNulltcTinyinttcSmallinttcIntegertcBiginttcDecimaltcRealtcDoubletcChartcVarchartcNchartcNvarchartcBinarytcVarbinarytcDatetcTimetcTimestamp" _TypeCode_name_1 = "tcClobtcNclobtcBlobtcBooleantcStringtcNstringtcBlocatortcNlocatortcBstring" _TypeCode_name_2 = "tcVarchar2tcVarchar3tcNvarchar3tcVarbinary3" _TypeCode_name_3 = "tcSmalldecimal" _TypeCode_name_4 = "tcArraytcTexttcShorttext" _TypeCode_name_5 = "tcAlphanum" _TypeCode_name_6 = "tcLongdatetcSeconddatetcDaydatetcSecondtime" ) var ( _TypeCode_index_0 = [...]uint8{0, 6, 15, 25, 34, 42, 51, 57, 65, 71, 80, 87, 97, 105, 116, 122, 128, 139} _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_4 = [...]uint8{0, 7, 13, 24} _TypeCode_index_6 = [...]uint8{0, 10, 22, 31, 43} ) 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 50 <= i && i <= 52: i -= 50 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]] default: return "TypeCode(" + strconv.FormatInt(int64(i), 10) + ")" } } golang-github-sap-go-hdb-0.14.1/internal/scanner/000077500000000000000000000000001346210761100215115ustar00rootroot00000000000000golang-github-sap-go-hdb-0.14.1/internal/scanner/scanner.go000066400000000000000000000140631346210761100234750ustar00rootroot00000000000000// +build go1.10 /* 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 scanner implements a HANA SQL query scanner. For a detailed HANA SQL query syntax please see https://help.sap.com/doc/6254b3bb439c4f409a979dc407b49c9b/2.0.00/en-US/SAP_HANA_SQL_Script_Reference_en.pdf */ package scanner import ( "errors" "fmt" "io" "strings" "sync" "unicode" ) // ErrToken is raised when a token is malformed (e.g. string with missing ending quote). var ErrToken = errors.New("Invalid token") // Token constants. const ( EOF = -(iota + 1) Error Undefined Operator Delimiter IdentifierDelimiter Identifier QuotedIdentifier Variable PosVariable NamedVariable String Number ) var tokenString = map[rune]string{ EOF: "EOF", Error: "Error", Undefined: "Undefined", Operator: "Operator", Delimiter: "Delimiter", IdentifierDelimiter: "IdentifierDelimiter", Identifier: "Identifier", QuotedIdentifier: "QuotedIdentifier", Variable: "Variable", PosVariable: "PosVariable", NamedVariable: "NamedVariable", String: "String", Number: "Number", } // TokenString returns a printable string for a token or Unicode character. func TokenString(tok rune) string { if s, ok := tokenString[tok]; ok { return s } return fmt.Sprintf("%q", string(tok)) } var compositeOperators = map[string]struct{}{"<>": struct{}{}, "<=": struct{}{}, ">=": struct{}{}, "!=": struct{}{}} func isOperator(ch rune) bool { return strings.ContainsRune("<>=!", ch) } func isCompositeOperator(s string) bool { _, ok := compositeOperators[s]; return ok } func isDelimiter(ch rune) bool { return strings.ContainsRune(",;(){}[]", ch) } func isNameDelimiter(ch rune) bool { return ch == '.' } func isDigit(ch rune) bool { return unicode.IsDigit(ch) } func isNumber(ch rune) bool { return ch == '+' || ch == '-' || isDigit(ch) } func isExp(ch rune) bool { return ch == 'e' || ch == 'E' } func isDecimalSeparator(ch rune) bool { return ch == '.' } func isIdentifier(ch rune) bool { return ch == '_' || unicode.IsLetter(ch) } func isAlpha(ch rune) bool { return ch == '#' || ch == '$' || isIdentifier(ch) || isDigit(ch) } func isSingleQuote(ch rune) bool { return ch == '\'' } func isDoubleQuote(ch rune) bool { return ch == '"' } func isQuestionMark(ch rune) bool { return ch == '?' } func isColon(ch rune) bool { return ch == ':' } var scannerPool = sync.Pool{} // Scanner is a HANA SQL query string scanner. type Scanner struct { rd *strings.Reader b strings.Builder ch rune // next char err error // last error cnt int } // NewScanner creates a new Scaner instance. func NewScanner(str string) *Scanner { s, _ := scannerPool.Get().(*Scanner) if s == nil { s = &Scanner{rd: strings.NewReader(str)} } else { s.rd.Reset(str) } s.ch = -2 s.err = nil return s } /* FreeScanner pools the Scanner instance for later usage. Scanner instances are not thread safe. After the call of FreeScanner calls of Scanner methods lead to undefined behavior. */ func (s *Scanner) FreeScanner() { scannerPool.Put(s) } func (s *Scanner) read() rune { ch, _, err := s.rd.ReadRune() switch err { case nil: return ch case io.EOF: return EOF default: return Error } } func (s *Scanner) peek() rune { if s.ch == -2 { s.ch = s.read() } return s.ch } func (s *Scanner) next() rune { ch := s.peek() if ch != EOF { s.ch = s.read() } return ch } // Next returns the next Token. func (s *Scanner) Next() rune { s.b.Reset() ch := s.next() for unicode.IsSpace(ch) { ch = s.next() } tok := ch switch { default: tok = Undefined ch = s.next() case ch == EOF: break case isOperator(ch): tok = Operator ch = s.scanOperator(ch) case isDelimiter(ch): tok = Delimiter s.b.WriteRune(ch) ch = s.next() case isNameDelimiter(ch): tok = IdentifierDelimiter s.b.WriteRune(ch) ch = s.next() case isIdentifier(ch): tok = Identifier s.b.WriteRune(ch) ch = s.scanAlpha() case isSingleQuote(ch): tok = String ch = s.scanQuotedIdentifier(ch) case isDoubleQuote(ch): tok = QuotedIdentifier ch = s.scanQuotedIdentifier(ch) case isQuestionMark(ch): tok = Variable ch = s.next() case isColon(ch): ch = s.peek() if isDigit(ch) { tok = PosVariable ch = s.scanNumeric() } else { tok = NamedVariable ch = s.scanAlpha() } case isNumber(ch): tok = Number ch = s.scanNumber() } s.ch = ch s.rd.UnreadRune() if ch == Error { return ch } return tok } // Value returns the value of the Token returned by Next. func (s *Scanner) Value() string { return s.b.String() } func (s *Scanner) scanOperator(ch1 rune) rune { s.b.WriteRune(ch1) ch2 := s.next() if isCompositeOperator(string([]rune{ch1, ch2})) { s.b.WriteRune(ch2) return s.next() } return ch2 } func (s *Scanner) scanAlpha() rune { ch := s.next() for isAlpha(ch) { s.b.WriteRune(ch) ch = s.next() } return ch } func (s *Scanner) scanNumeric() rune { ch := s.next() for isDigit(ch) { ch = s.next() } return ch } func (s *Scanner) scanQuotedIdentifier(quote rune) rune { ch := s.next() for { if ch == quote { ch = s.next() if ch != quote { return ch } } s.b.WriteRune(ch) ch = s.next() if ch == EOF || ch == Error { return Error } } } func (s *Scanner) scanNumber() rune { ch := s.scanNumeric() if isDecimalSeparator(ch) { ch = s.scanNumeric() } if isExp(ch) { ch = s.next() if isNumber(ch) { ch = s.scanNumeric() } } return ch } golang-github-sap-go-hdb-0.14.1/internal/scanner/scanner_test.go000066400000000000000000000065551346210761100245430ustar00rootroot00000000000000// +build go1.10 /* 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 scanner import ( "fmt" "reflect" "testing" ) var testSQL = []string{ ``, //empty ` `, // only whitespaces `delete from Invoice where TimeCreated < :end and TimeCreated >= :start;`, `delete from _Invoice where _TimeCreated < :end and _TimeCreated >= :start;`, `delete from "Invoice" where "TimeCreated" < :end and "TimeCreated" >= :start;`, `delete from schema."Invoice";`, `delete from "schema"."Invoice";`, `delete from "Inv"""oice" where "Time""Created" < :end and "Time""Created" >= :start;`, } type tokenValue struct { tok rune val string } func (tv *tokenValue) String() string { return fmt.Sprintf("%s: %s\n", TokenString(tv.tok), tv.val) } var testResults = [][]*tokenValue{ {}, {}, { {Identifier, "delete"}, {Identifier, "from"}, {Identifier, "Invoice"}, {Identifier, "where"}, {Identifier, "TimeCreated"}, {Operator, "<"}, {NamedVariable, "end"}, {Identifier, "and"}, {Identifier, "TimeCreated"}, {Operator, ">="}, {NamedVariable, "start"}, {Delimiter, ";"}, }, { {Identifier, "delete"}, {Identifier, "from"}, {Identifier, "_Invoice"}, {Identifier, "where"}, {Identifier, "_TimeCreated"}, {Operator, "<"}, {NamedVariable, "end"}, {Identifier, "and"}, {Identifier, "_TimeCreated"}, {Operator, ">="}, {NamedVariable, "start"}, {Delimiter, ";"}, }, { {Identifier, "delete"}, {Identifier, "from"}, {QuotedIdentifier, "Invoice"}, {Identifier, "where"}, {QuotedIdentifier, "TimeCreated"}, {Operator, "<"}, {NamedVariable, "end"}, {Identifier, "and"}, {QuotedIdentifier, "TimeCreated"}, {Operator, ">="}, {NamedVariable, "start"}, {Delimiter, ";"}, }, { {Identifier, "delete"}, {Identifier, "from"}, {Identifier, "schema"}, {IdentifierDelimiter, "."}, {QuotedIdentifier, "Invoice"}, {Delimiter, ";"}, }, { {Identifier, "delete"}, {Identifier, "from"}, {QuotedIdentifier, "schema"}, {IdentifierDelimiter, "."}, {QuotedIdentifier, "Invoice"}, {Delimiter, ";"}, }, { {Identifier, "delete"}, {Identifier, "from"}, {QuotedIdentifier, `Inv"`}, {Identifier, "oice"}, {QuotedIdentifier, ` where `}, {Identifier, "Time"}, {QuotedIdentifier, ``}, {Identifier, "Created"}, {QuotedIdentifier, ` < :end and `}, {Identifier, "Time"}, {QuotedIdentifier, ``}, {Identifier, "Created"}, {Error, ` >= :start;`}, }, } func scan(t *testing.T, sql string, tv *([]*tokenValue)) { s := NewScanner(sql) defer s.FreeScanner() for { tok := s.Next() if tok == EOF { break } *tv = append(*tv, &tokenValue{tok: tok, val: s.Value()}) } } func TestSQL(t *testing.T) { tv := make([]*tokenValue, 0) for i, sql := range testSQL { tv = tv[:0] scan(t, sql, &tv) if !reflect.DeepEqual(testResults[i], tv) { t.Logf("%v", tv) t.Logf("%v", testResults[i]) t.Errorf("test %d failed", i) } } } golang-github-sap-go-hdb-0.14.1/internal/unicode/000077500000000000000000000000001346210761100215065ustar00rootroot00000000000000golang-github-sap-go-hdb-0.14.1/internal/unicode/cesu8/000077500000000000000000000000001346210761100225355ustar00rootroot00000000000000golang-github-sap-go-hdb-0.14.1/internal/unicode/cesu8/cesu8.go000066400000000000000000000123661346210761100241230ustar00rootroot00000000000000/* 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 } golang-github-sap-go-hdb-0.14.1/internal/unicode/cesu8/cesu8_test.go000066400000000000000000000051221346210761100251520ustar00rootroot00000000000000/* 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 import ( "bytes" "testing" "unicode/utf8" ) func TestCodeLen(t *testing.T) { b := make([]byte, CESUMax) for i := rune(0); i <= utf8.MaxRune; i++ { n := EncodeRune(b, i) if n != RuneLen(i) { t.Fatalf("rune length check error %d %d", n, RuneLen(i)) } } } type testCP struct { cp rune utf8 []byte } // see http://en.wikipedia.org/wiki/CESU-8 var testCPData = []*testCP{ &testCP{0x45, []byte{0x45}}, &testCP{0x205, []byte{0xc8, 0x85}}, &testCP{0x10400, []byte{0xed, 0xa0, 0x81, 0xed, 0xb0, 0x80}}, } func TestCP(t *testing.T) { b := make([]byte, CESUMax) for _, d := range testCPData { n1 := EncodeRune(b, d.cp) if bytes.Compare(b[:n1], d.utf8) != 0 { t.Logf("encode code point %x char %c cesu-8 %x - expected %x", d.cp, d.cp, b[:n1], d.utf8) } else { t.Logf("encode code point %x char %c cesu-8 %x", d.cp, d.cp, b[:n1]) } cp, n2 := DecodeRune(b[:n1]) if cp != d.cp || n2 != n1 { t.Logf("decode code point %x size %d - expected %x size %d", cp, n2, d.cp, n1) } else { t.Logf("decode code point %x size %d", cp, n2) } } } // took from https://golang.org/src/unicode/utf8/utf8_test.go var testStrings = []string{ "", "abcd", "☺☻☹", "日a本b語ç日ð本Ê語þ日¥本¼語i日©", "日a本b語ç日ð本Ê語þ日¥本¼語i日©日a本b語ç日ð本Ê語þ日¥本¼語i日©日a本b語ç日ð本Ê語þ日¥本¼語i日©", "\x80\x80\x80\x80", } func TestString(t *testing.T) { b := make([]byte, CESUMax) for i, s := range testStrings { n := 0 for _, r := range s { n += utf8.EncodeRune(b, r) if r >= 0xFFFF { // CESU-8: 6 Bytes n += 2 } } // 1. Test: cesu string size m := StringSize(s) if m != n { t.Fatalf("%d invalid string size %d - expected %d", i, m, n) } // 2. Test: cesu slice len m = Size([]byte(s)) if m != n { t.Fatalf("%d invalid slice size %d - expected %d", i, m, n) } // 3. Test: convert len m = 0 for _, r := range s { m += EncodeRune(b, r) } if m != n { t.Fatalf("%d invalid encoder size %d - expected %d", i, m, n) } } } golang-github-sap-go-hdb-0.14.1/internal/unicode/unicode.go000066400000000000000000000056361346210761100234750ustar00rootroot00000000000000/* 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 } golang-github-sap-go-hdb-0.14.1/trust.pem000066400000000000000000000023651346210761100201360ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIDezCCAmOgAwIBAgIJALkmvHJMri88MA0GCSqGSIb3DQEBBQUAMFQxCzAJBgNV BAYTAlVTMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX aWRnaXRzIFB0eSBMdGQxDTALBgNVBAMMBGhhbmEwHhcNMTgwMzE5MTMyMjE0WhcN MjgwMzE2MTMyMjE0WjBUMQswCQYDVQQGEwJVUzETMBEGA1UECAwKU29tZS1TdGF0 ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMQ0wCwYDVQQDDARo YW5hMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2SSNMuOLMF052AWZ JBdlc2ronl/Wf/iI4TCQHplHG/ITNY6hBX6pQunzMNKBaYdRh5YEh6Yy6ZmDE//C n2dldsEAm/NC6wJZipDWRmx9E+md73b8o5FJlVhuaRpeZr8Nwkj5C+UVcw0S7HGh oJ1YNom3TpShEVL0jRdnXRQWUb3nxDQPm4SgAT6w6GKG41b0ucLTBTV2WOvt4tlu t+eGYG16Xr/ix3XXSaBIQaSxvDn/P5mZfeVUqsqnuSWjMAxdXunBK5rtwywGG732 nE8VxcL2znhJlM49UINIStpvk+R7pRPuaI/R1ei/CpT6oF2ooxF2h+ZJCjkUfd5T pgMrWQIDAQABo1AwTjAdBgNVHQ4EFgQU5Y2G4CoWq1WfMs+O+1YcxTUEVIwwHwYD VR0jBBgwFoAU5Y2G4CoWq1WfMs+O+1YcxTUEVIwwDAYDVR0TBAUwAwEB/zANBgkq hkiG9w0BAQUFAAOCAQEAq4VJOgsHiCfkog3rH9xBPoUPZUDHuzxQSfK8zZGBHvWU ut7CIq6OurpQHGzgUklz83MontbVyIjk7MZ3t+Big4ssBPPI0Bz1UbwoPrzCKIqM 3Zp4qGjw8paVlfrKoSvTW8tUic/DVRe+ekav1JAPoNUV0LA7Dn/azSS4Y+EIHR1i jiKxOJi+AMER39ofqNvTNjKuGKwUQCA3CBbwt6lKeDvniimCSVaqh7HxJNewJll0 Fgl2Duv0LS3EOzTS9ph+v3SB9Kj0a7TkHLVKi7WxGAEDJ45NqJyw8HB9DbUlLfoH QDkj7gcthh8Nzy5RHQlIfZ5x01CXZVdF0b1s+5rDyw== -----END CERTIFICATE-----