pax_global_header00006660000000000000000000000064137025615460014523gustar00rootroot0000000000000052 comment=5db0d22ba0db5f2f26dd327c968f9bbcfa498137 golang-github-sap-go-hdb-0.100.10/000077500000000000000000000000001370256154600164105ustar00rootroot00000000000000golang-github-sap-go-hdb-0.100.10/.github/000077500000000000000000000000001370256154600177505ustar00rootroot00000000000000golang-github-sap-go-hdb-0.100.10/.github/workflows/000077500000000000000000000000001370256154600220055ustar00rootroot00000000000000golang-github-sap-go-hdb-0.100.10/.github/workflows/build.yml000066400000000000000000000006521370256154600236320ustar00rootroot00000000000000name: build on: [push] jobs: build: name: Build runs-on: ubuntu-latest steps: - name: Set up Go uses: actions/setup-go@v1 with: go-version: 1.14 id: go - name: Check out code into the Go module directory uses: actions/checkout@v1 - name: Get dependencies run: | go get -v -t -d ./... - name: Build run: | go build -v ./... golang-github-sap-go-hdb-0.100.10/.gitignore000066400000000000000000000005101370256154600203740ustar00rootroot00000000000000# 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*.* *.pdf *.txt *.debug recordingsgolang-github-sap-go-hdb-0.100.10/.gitlab-ci.yml000066400000000000000000000012211370256154600210400ustar00rootroot00000000000000variables: 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: - export GOHDBDSN=$GO_HANA_DSN - go test -v ./... - export GOHDBDSN=$GO_HANA_TLS_DSN - go test -v ./... golang_1_13_10: image: golang:1.13.8 <<: *test_definition golang_1_14_2: image: golang:1.14.2 <<: *test_definition golang-github-sap-go-hdb-0.100.10/.golangci.yml000066400000000000000000000055241370256154600210020ustar00rootroot00000000000000linters-settings: depguard: list-type: blacklist packages: # logging is allowed only by logutils.Log, logrus # is allowed to use only in logutils package - github.com/sirupsen/logrus packages-with-error-message: - github.com/sirupsen/logrus: "logging is allowed only by logutils.Log" dupl: threshold: 100 funlen: lines: 100 statements: 50 goconst: min-len: 2 min-occurrences: 2 gocritic: enabled-tags: - diagnostic - experimental - opinionated - performance - style disabled-checks: - dupImport # https://github.com/go-critic/go-critic/issues/845 - ifElseChain - octalLiteral - whyNoLint - wrapperFunc gocyclo: min-complexity: 15 goimports: local-prefixes: github.com/golangci/golangci-lint golint: min-confidence: 0 gomnd: settings: mnd: # don't include the "operation" and "assign" checks: argument,case,condition,return govet: check-shadowing: true settings: printf: funcs: - (github.com/golangci/golangci-lint/pkg/logutils.Log).Infof - (github.com/golangci/golangci-lint/pkg/logutils.Log).Warnf - (github.com/golangci/golangci-lint/pkg/logutils.Log).Errorf - (github.com/golangci/golangci-lint/pkg/logutils.Log).Fatalf lll: line-length: 140 maligned: suggest-new: true misspell: locale: US linters: # please, do not use `enable-all`: it's deprecated and will be removed soon. # inverted configuration with `enable-all` and `disable` is not scalable during updates of golangci-lint disable-all: true enable: - bodyclose - deadcode - depguard - dogsled - dupl - errcheck - gochecknoinits - goconst - gocritic - gocyclo - gofmt - goimports - golint - gomnd - goprintffuncname - gosec - gosimple - govet - ineffassign - interfacer - misspell - nakedret - rowserrcheck - scopelint - staticcheck - structcheck - stylecheck - typecheck - unconvert - unparam - unused - varcheck - whitespace - maligned - prealloc # don't enable: # - funlen # - lll # - gochecknoglobals # - gocognit # - godox # - maligned # - prealloc issues: # Excluding configuration per-path, per-linter, per-text and per-source exclude-rules: - path: _test\.go linters: - gomnd run: skip-dirs: - test/testdata_etc - internal/cache - internal/renameio - internal/robustio # golangci.com configuration # https://github.com/golangci/golangci/wiki/Configuration service: golangci-lint-version: 1.23.x # use the fixed version to not introduce new linters unexpectedly prepare: - echo "here I can run custom commands, but no preparation needed for this repo" golang-github-sap-go-hdb-0.100.10/LICENSE000066400000000000000000000260731370256154600174250ustar00rootroot00000000000000Apache 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.100.10/NOTICE000066400000000000000000000002301370256154600173070ustar00rootroot00000000000000SAP 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.100.10/README.md000066400000000000000000000042711370256154600176730ustar00rootroot00000000000000go-hdb ====== [![GoDoc](https://godoc.org/github.com/SAP/go-hdb/driver?status.png)](https://godoc.org/github.com/SAP/go-hdb/driver) [![Go Report Card](https://goreportcard.com/badge/github.com/SAP/go-hdb)](https://goreportcard.com/report/github.com/SAP/go-hdb) ![](https://github.com/SAP/go-hdb/workflows/build/badge.svg) 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.14 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). * Support of [PBKDF2](https://tools.ietf.org/html/rfc2898) authentication as default and standard user / password as fallback. ## Dependencies * * golang-github-sap-go-hdb-0.100.10/RELEASENOTES.md000066400000000000000000000066221370256154600206310ustar00rootroot00000000000000Release Notes ============= ## Release 0.100 #### Minor revisions Release 0.100.10 - rename master to main branch Release 0.100.9 - added custom dialers for driver - database connections - added float to integer conversion in case the float value can be represented as integer - added no-timeout option for db reads and writes (timeout == 0) - added connection ping interval to database connector object Release 0.100.8 - fixed authentication issue for DB instances not supporting PBKDF2 - added tcp keep-alive parameter to connector Release 0.100.6 - 0.100.7 - added alpha version of Bintext support Release 0.100.5 - go 1.13 compatibility Release 0.100.1 - 0.100.4 - minor optimizations - bug fixes - linter fixes - additional lob example (read / write lob in chunks via pipe) #### Release Notes - Added support of [PBKDF2](https://tools.ietf.org/html/rfc2898) authentication. PBKDF2 authentification is now used as default. Standard user / password authentication is used as fallback solution. ## Release 0.99 #### Minor revisions Release 0.99.1 - Additional conversions for query parameters - now strings can be used for integer and float types #### Release Notes Dear go-hdb users, please find a description of the main features of this release below. Going from 0.14.4 to 0.99 should indicate, that this is a huge step into the direction of a 1.0 release. So, while most effort was spent to prepare for 1.0 features like - Support of [Named Arguments](https://golang.org/pkg/database/sql/#NamedArg) - Support of [Output Parameters](https://golang.org/pkg/database/sql/#Out) calling Stored Procedures this release brings some interesting and hopefully useful features as well: #### Main Features 1. Data Format Version. The so-called Data Format Version was increased, so that the following HANA data types will be recognized by the driver and correctly reported by https://golang.org/pkg/database/sql/#ColumnType.DatabaseTypeName - DAYDATE - SECONDTIME - LONGDATE - SECONDDATE - SHORTTEXT - ALPHANUM 2. Usage of [sql.Rows](https://golang.org/pkg/database/sql/#Rows) in Stored Procedures with table output parameters. Until now, table output parameter content was retrieved via a separate query call. As the Go sql package does now support sql.Rows in [Rows.Scan](https://golang.org/pkg/database/sql/#Rows.Scan), the workaround via a separate query call is obsolete. Nonetheless, like this change is incompatible compared to the former releases, the feature needs to be opted in. To support a smooth transition, procedure calls works per default like in the past (legacy mode). Anyway, the use of 'separate queries' is deprecated and the default is going to be changed within the next releases. Release 1.0 will only support the new version of retrieving table output parameter content, so new projects based on (go-hdb)[https://github.com/SAP/go-hdb] should opt in the feature already now: - please use a Connector object to open a database - please set the legacy mode via the Connector object to false ```golang connector, err := NewDSNConnector(TestDSN) if err != nil { log.Fatal(err) } // *Switch to non-legacy mode. connector.SetLegacy(false) db := sql.OpenDB(connector) defer db.Close() ``` For a complete example please see [Example_callTableOut](driver/example_call_test.go). #### Incompatibilities - no known incompatibilitiesgolang-github-sap-go-hdb-0.100.10/cmd/000077500000000000000000000000001370256154600171535ustar00rootroot00000000000000golang-github-sap-go-hdb-0.100.10/cmd/sniffer/000077500000000000000000000000001370256154600206075ustar00rootroot00000000000000golang-github-sap-go-hdb-0.100.10/cmd/sniffer/scripts/000077500000000000000000000000001370256154600222765ustar00rootroot00000000000000golang-github-sap-go-hdb-0.100.10/cmd/sniffer/scripts/test01.go000066400000000000000000000015411370256154600237460ustar00rootroot00000000000000/* Copyright 2020 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 main import ( "database/sql" "log" "os" "github.com/SAP/go-hdb/driver" ) func main() { dsn := os.Getenv("GOHDBDSN") db, err := sql.Open(driver.DriverName, dsn) if err != nil { log.Fatal(err) } defer db.Close() _, err = db.Query("select * from T1") if err != nil { log.Fatal(err) } } golang-github-sap-go-hdb-0.100.10/cmd/sniffer/scripts/test01.py000066400000000000000000000013471370256154600237750ustar00rootroot00000000000000from hdbcli import dbapi conn = dbapi.connect( address="localhost", port=50000, user="SYSTEM", password="Toor1234" ) cursor = conn.cursor() cursor.execute("DROP TABLE T1") cursor.execute("CREATE TABLE T1 (ID INTEGER PRIMARY KEY, C2 VARCHAR(255))") cursor.close() sql = 'INSERT INTO T1 (ID, C2) VALUES (?, ?)' cursor = conn.cursor() cursor.execute(sql, (1, 'hello')) # returns True cursor.execute(sql, (2, 'hello again')) # returns True cursor.close() sql = 'INSERT INTO T1 (ID, C2) VALUES (:id, :c2)' cursor = conn.cursor() id = 3 c2 = "goodbye" cursor.execute(sql, {"id": id, "c2": c2}) # returns True cursor.close() sql = 'SELECT * FROM T1' cursor = conn.cursor() cursor.execute(sql) for row in cursor: print(row) golang-github-sap-go-hdb-0.100.10/cmd/sniffer/sniffer.go000066400000000000000000000054131370256154600225750ustar00rootroot00000000000000/* Copyright 2020 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 main import ( "flag" "fmt" "io" "log" "net" "os" p "github.com/SAP/go-hdb/internal/protocol" ) func main() { addr, dbAddr := cli() log.Printf("listening on %s (database address %s)", addr.String(), dbAddr.String()) l, err := net.Listen(addr.Network(), addr.String()) if err != nil { log.Fatal(err) } defer l.Close() for { conn, err := l.Accept() if err != nil { log.Fatal(err) } go handler(conn, dbAddr) } } func handler(conn net.Conn, dbAddr net.Addr) { dbConn, err := net.Dial(dbAddr.Network(), dbAddr.String()) if err != nil { log.Printf("hdb connection error: %s", err) return } defer dbConn.Close() err = p.NewSniffer(conn, dbConn).Do() if err == nil { return } switch err { case io.EOF: log.Printf("client connection closed - local address %s - remote address %s", conn.LocalAddr().String(), conn.RemoteAddr().String(), ) default: log.Printf("sniffer protocol error: %s - close connection - local address %s - remote address %s", err, conn.LocalAddr().String(), conn.RemoteAddr().String(), ) } } const ( defaultAddr = "localhost:50000" defaultDBAddr = "localhost:39013" ) type addrValue struct { addr string } func (v *addrValue) String() string { return v.addr } func (v *addrValue) Network() string { return "tcp" } func (v *addrValue) Set(s string) error { if _, _, err := net.SplitHostPort(s); err != nil { return err } v.addr = s return nil } func cli() (net.Addr, net.Addr) { const usageText = ` %[1]s is a Hana Network Protocol analyzer. It lets you see whats happening on protocol level connecting a client to the database server. %[1]s is an early alpha-version, supporting mainly go-hdb based clients. Using with other clients might - completely fail or - provide incomplete output Usage of %[1]s: ` addr := &addrValue{addr: defaultAddr} dbAddr := &addrValue{addr: defaultDBAddr} args := flag.NewFlagSet("", flag.ExitOnError) args.Usage = func() { fmt.Fprintf(args.Output(), usageText, os.Args[0]) args.PrintDefaults() } args.Var(addr, "s", ": Sniffer address to accept connections. (required)") args.Var(dbAddr, "db", ": Database address to connect to. (required)") args.Parse(os.Args[1:]) return addr, dbAddr } golang-github-sap-go-hdb-0.100.10/driver/000077500000000000000000000000001370256154600177035ustar00rootroot00000000000000golang-github-sap-go-hdb-0.100.10/driver/bulk_test.go000066400000000000000000000107111370256154600222260ustar00rootroot00000000000000// +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 ) // TestBulkFrame func testBulkFrame(db *sql.DB, cmd string, insertFct func(stmt *sql.Stmt), t *testing.T) { // 1. prepare tmpTableName := RandomIdentifier("#tmpTable") //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("%s %s values (?)", cmd, tmpTableName)) if err != nil { t.Fatalf("prepare bulk insert failed: %s", err) } defer stmt.Close() // 2. call insert function insertFct(stmt) // 3. check 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(db *sql.DB, t *testing.T) { table := RandomIdentifier("bulkInsertDuplicates") if _, err := db.Exec(fmt.Sprintf("create table %s (k integer primary key, v integer)", table)); err != nil { t.Fatalf("create table failed: %s", err) } stmt, err := db.Prepare(fmt.Sprintf("bulk insert into %s values (?,?)", 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]) } } } func testBulk(db *sql.DB, t *testing.T) { tests := []struct { name string cmd string insertFct func(stmt *sql.Stmt) }{ { "bulkInsertViaCommand", "bulk insert into", func(stmt *sql.Stmt) { 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) } }, }, { "bulkInsertViaParameter", "insert into", func(stmt *sql.Stmt) { 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) } } }, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { testBulkFrame(db, test.cmd, test.insertFct, t) }) } } func TestBulk(t *testing.T) { tests := []struct { name string fct func(db *sql.DB, t *testing.T) }{ {"testBulk", testBulk}, {"testBulkInsertDuplicates", testBulkInsertDuplicates}, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { test.fct(TestDB, t) }) } } golang-github-sap-go-hdb-0.100.10/driver/bytes.go000066400000000000000000000022141370256154600213570ustar00rootroot00000000000000/* 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.100.10/driver/call_test.go000066400000000000000000000145631370256154600222150ustar00rootroot00000000000000// +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" "reflect" "testing" ) func testCallEchoQueryRow(db *sql.DB, proc Identifier, t *testing.T) { const txt = "Hello World!" var out string if err := db.QueryRow(fmt.Sprintf("call %s(?, ?)", proc), txt).Scan(&out); err != nil { t.Fatal(err) } if out != txt { t.Fatalf("value %s - expected %s", out, txt) } } func testCallBlobEcho(db *sql.DB, t *testing.T) { const procBlobEcho = `create procedure %[1]s (in idata nclob, out odata nclob) language SQLSCRIPT as begin odata := idata; end ` const txt = "Hello World - 𝄞𝄞€€!" proc := RandomIdentifier("procBlobEcho_") if _, err := db.Exec(fmt.Sprintf(procBlobEcho, proc)); 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(?, ?)", proc), inlob).Scan(outlob); err != nil { t.Fatal(err) } out := b.String() if out != txt { t.Fatalf("value %s - expected %s", out, txt) } } func testCallEcho(db *sql.DB, t *testing.T) { const procEcho = `create procedure %[1]s (in idata nvarchar(25), out odata nvarchar(25)) language SQLSCRIPT as begin odata := idata; end ` // create procedure proc := RandomIdentifier("procEcho_") if _, err := db.Exec(fmt.Sprintf(procEcho, proc)); err != nil { t.Fatal(err) } tests := []struct { name string fct func(db *sql.DB, proc Identifier, t *testing.T) }{ {"QueryRow", testCallEchoQueryRow}, // {"Query", testCallEchoQuery}, // {"Exec", testCallEchoExec}, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { test.fct(TestDB, proc, t) }) } } func testCallTableOut(db *sql.DB, 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 ` testData := [][]struct { i int x string }{ {{0, "A"}, {1, "B"}, {2, "C"}, {3, "D"}, {4, "E"}}, {{0, "A"}, {1, "B"}, {2, "C"}, {3, "D"}, {4, "E"}, {5, "F"}, {6, "G"}, {7, "H"}, {8, "I"}, {9, "J"}}, {{0, "A"}, {1, "B"}, {2, "C"}, {3, "D"}, {4, "E"}, {5, "F"}, {6, "G"}, {7, "H"}, {8, "I"}, {9, "J"}, {10, "K"}, {11, "L"}, {12, "M"}, {13, "N"}, {14, "O"}}, } stringType := reflect.TypeOf((*string)(nil)).Elem() rowsType := reflect.TypeOf((*sql.Rows)(nil)).Elem() createObj := func(t reflect.Type) interface{} { return reflect.New(t).Interface() } createString := func() interface{} { return createObj(stringType) } createRows := func() interface{} { return createObj(rowsType) } testCheck := func(testSet int, rows *sql.Rows, t *testing.T) { 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 != testData[testSet][j].i { t.Fatalf("value i %d - expected %d", i, testData[testSet][j].i) } if x != testData[testSet][j].x { t.Fatalf("value x %s - expected %s", x, testData[testSet][j].x) } j++ } if err := rows.Err(); err != nil { log.Fatal(err) } } testCall := func(db *sql.DB, proc Identifier, legacy bool, targets []interface{}, t *testing.T) { rows, err := db.Query(fmt.Sprintf("call %s.%s(?, ?, ?, ?)", TestSchema, proc), 1) if err != nil { t.Fatal(err) } defer rows.Close() if !rows.Next() { log.Fatal(rows.Err()) } if err := rows.Scan(targets...); err != nil { log.Fatal(err) } for i, target := range targets { if legacy { // read table parameter by separate query rows, err := db.Query(*target.(*string)) if err != nil { t.Fatal(err) } testCheck(i, rows, t) rows.Close() } else { // use rows directly testCheck(i, target.(*sql.Rows), t) } } } tableType := RandomIdentifier("tt2_") proc := RandomIdentifier("procTableOut_") // create table type if _, err := db.Exec(fmt.Sprintf("create type %s.%s as table (i integer, x varchar(10))", TestSchema, tableType)); err != nil { t.Fatal(err) } // create procedure if _, err := db.Exec(fmt.Sprintf(procTableOut, TestSchema, proc, tableType)); err != nil { t.Fatal(err) } connector, err := NewDSNConnector(TestDSN) if err != nil { t.Fatal(err) } connector.SetDefaultSchema(TestSchema) tests := []struct { name string legacy bool fct func(db *sql.DB, proc Identifier, legacy bool, targets []interface{}, t *testing.T) targets []interface{} }{ {"tableOutRef", true, testCall, []interface{}{createString(), createString(), createString()}}, {"tableOutRows", false, testCall, []interface{}{createRows(), createRows(), createRows()}}, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { connector.SetLegacy(test.legacy) db := sql.OpenDB(connector) defer db.Close() test.fct(db, proc, test.legacy, test.targets, t) }) } } func TestCall(t *testing.T) { tests := []struct { name string fct func(db *sql.DB, t *testing.T) }{ {"echo", testCallEcho}, {"blobEcho", testCallBlobEcho}, {"tableOut", testCallTableOut}, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { test.fct(TestDB, t) }) } } golang-github-sap-go-hdb-0.100.10/driver/columntype_test.go000066400000000000000000000173301370256154600234740ustar00rootroot00000000000000/* 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" p "github.com/SAP/go-hdb/internal/protocol" ) func testColumnType(connector *Connector, dataType func(string, int) string, dfv int, t *testing.T) { var ( testTime = time.Now() testDecimal = (*Decimal)(big.NewRat(1, 1)) testString = "HDB column type" testBinary = []byte{0x00, 0x01, 0x02} ) testColumnTypeData := []struct { sqlType string length int64 varLength bool decimalType bool typeName string precision int64 scale int64 nullable bool scanType reflect.Type value interface{} }{ {"tinyint", 0, false, false, "TINYINT", 0, 0, true, p.DtTinyint.ScanType(), 1}, {"smallint", 0, false, false, "SMALLINT", 0, 0, true, p.DtSmallint.ScanType(), 42}, {"integer", 0, false, false, "INTEGER", 0, 0, true, p.DtInteger.ScanType(), 4711}, {"bigint", 0, false, false, "BIGINT", 0, 0, true, p.DtBigint.ScanType(), 68000}, {"decimal", 0, false, true, "DECIMAL", 34, 32767, true, p.DtDecimal.ScanType(), testDecimal}, {"real", 0, false, false, "REAL", 0, 0, true, p.DtReal.ScanType(), 1.0}, {"double", 0, false, false, "DOUBLE", 0, 0, true, p.DtDouble.ScanType(), 3.14}, {"char", 30, true, false, "CHAR", 0, 0, true, p.DtString.ScanType(), testString}, {"varchar", 30, true, false, "VARCHAR", 0, 0, true, p.DtString.ScanType(), testString}, {"nchar", 20, true, false, "NCHAR", 0, 0, true, p.DtString.ScanType(), testString}, {"nvarchar", 20, true, false, "NVARCHAR", 0, 0, true, p.DtString.ScanType(), testString}, {"binary", 10, true, false, "BINARY", 0, 0, true, p.DtBytes.ScanType(), testBinary}, {"varbinary", 10, true, false, "VARBINARY", 0, 0, true, p.DtBytes.ScanType(), testBinary}, {"date", 0, false, false, dataType("DAYDATE", dfv), 0, 0, true, p.DtTime.ScanType(), testTime}, {"time", 0, false, false, dataType("SECONDTIME", dfv), 0, 0, true, p.DtTime.ScanType(), testTime}, {"timestamp", 0, false, false, dataType("LONGDATE", dfv), 0, 0, true, p.DtTime.ScanType(), testTime}, {"clob", 0, false, false, "CLOB", 0, 0, true, p.DtLob.ScanType(), new(Lob).SetReader(bytes.NewBuffer(testBinary))}, {"nclob", 0, false, false, "NCLOB", 0, 0, true, p.DtLob.ScanType(), new(Lob).SetReader(bytes.NewBuffer(testBinary))}, {"blob", 0, false, false, "BLOB", 0, 0, true, p.DtLob.ScanType(), new(Lob).SetReader(bytes.NewBuffer(testBinary))}, {"boolean", 0, false, false, "TINYINT", 0, 0, true, p.DtTinyint.ScanType(), false}, // hdb gives TINYINT back - not BOOLEAN {"smalldecimal", 0, false, true, "DECIMAL", 16, 32767, true, p.DtDecimal.ScanType(), 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, dataType("SHORTTEXT", dfv), 0, 0, true, p.DtString.ScanType(), testString}, {"alphanum", 15, true, false, dataType("ALPHANUM", dfv), 0, 0, true, p.DtString.ScanType(), testString}, {"longdate", 0, false, false, dataType("LONGDATE", dfv), 0, 0, true, p.DtTime.ScanType(), testTime}, {"seconddate", 0, false, false, dataType("SECONDDATE", dfv), 0, 0, true, p.DtTime.ScanType(), testTime}, {"daydate", 0, false, false, dataType("DAYDATE", dfv), 0, 0, true, p.DtTime.ScanType(), testTime}, {"secondtime", 0, false, false, dataType("SECONDTIME", dfv), 0, 0, true, p.DtTime.ScanType(), testTime}, // not nullable {"tinyint", 0, false, false, "TINYINT", 0, 0, false, p.DtTinyint.ScanType(), 42}, {"nvarchar", 25, true, false, "NVARCHAR", 0, 0, false, p.DtString.ScanType(), 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 := sql.OpenDB(connector) 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) } } } func TestColumnType(t *testing.T) { const ( dfvBaseline = 1 // baseline data format version. dfvSPS06 = 4 //see docu dfvBINTEXT = 6 dfvDefault = dfvSPS06 ) dataType := func(dt string, dfv int) string { if dfv == dfvBaseline { switch dt { case "DAYDATE": return "DATE" case "SECONDTIME": return "TIME" case "LONGDATE", "SECONDDATE": return "TIMESTAMP" case "SHORTTEXT", "ALPHANUM": return "NVARCHAR" } } return dt } var testSet []int if testing.Short() { testSet = []int{dfvDefault} } else { testSet = []int{dfvBaseline, dfvSPS06, dfvBINTEXT} } connector, err := NewDSNConnector(TestDSN) if err != nil { t.Fatal(err) } for _, dfv := range testSet { name := fmt.Sprintf("dfv_%d", dfv) t.Run(name, func(t *testing.T) { connector.SetDfv(dfv) testColumnType(connector, dataType, dfv, t) }) } } golang-github-sap-go-hdb-0.100.10/driver/connection.go000066400000000000000000000340201370256154600223700ustar00rootroot00000000000000/* 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" "reflect" "time" "github.com/SAP/go-hdb/driver/sqltrace" p "github.com/SAP/go-hdb/internal/protocol" "github.com/SAP/go-hdb/internal/protocol/scanner" ) // 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") // ErrNestedQuery is the error raised if a sql statement is executed before an "active" statement is closed. // Example: execute sql statement before rows of privious select statement are closed. var ErrNestedQuery = errors.New("nested sql queries are not supported") // queries const ( pingQuery = "select 1 from dummy" isolationLevelStmt = "set transaction isolation level %s" accessModeStmt = "set transaction %s" sessionVariable = "set %s=%s" defaultSchema = "set schema %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) ) func init() { p.RegisterScanType(p.DtDecimal, reflect.TypeOf((*Decimal)(nil)).Elem()) p.RegisterScanType(p.DtLob, reflect.TypeOf((*Lob)(nil)).Elem()) } // 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) _ driver.Execer = (*conn)(nil) //go 1.9 issue (ExecerContext is only called if Execer is implemented) _ driver.QueryerContext = (*conn)(nil) _ driver.Queryer = (*conn)(nil) //go 1.9 issue (QueryerContext is only called if Queryer is implemented) _ driver.NamedValueChecker = (*conn)(nil) _ driver.SessionResetter = (*conn)(nil) ) type conn struct { session *p.Session scanner *scanner.Scanner closed chan struct{} } func newConn(ctx context.Context, ctr *Connector) (driver.Conn, error) { session, err := p.NewSession(ctx, ctr) if err != nil { return nil, err } c := &conn{session: session, scanner: &scanner.Scanner{}, closed: make(chan struct{})} if err := c.init(ctx, ctr); err != nil { return nil, err } d := ctr.PingInterval() if d != 0 { go c.pinger(d, c.closed) } return c, nil } func (c *conn) init(ctx context.Context, ctr *Connector) error { if ctr.sessionVariables != nil { for k, v := range ctr.sessionVariables { if _, err := c.ExecContext(ctx, fmt.Sprintf(sessionVariable, fmt.Sprintf("'%s'", k), fmt.Sprintf("'%s'", v)), nil); err != nil { return err } } } if ctr.defaultSchema != "" { if _, err := c.ExecContext(ctx, fmt.Sprintf(defaultSchema, ctr.defaultSchema), nil); err != nil { return err } } return nil } func (c *conn) pinger(d time.Duration, done <-chan struct{}) { ticker := time.NewTicker(d) defer ticker.Stop() ctx := context.Background() for { select { case <-done: return case <-ticker.C: c.Ping(ctx) } } } func (c *conn) Ping(ctx context.Context) (err error) { c.session.Lock() defer c.session.Unlock() if c.session.IsBad() { return driver.ErrBadConn } if c.session.InQuery() { return ErrNestedQuery } // caution!!! defer c.session.SetInQuery(false) done := make(chan struct{}) go func() { _, err = c.session.QueryDirect(pingQuery) close(done) }() select { case <-ctx.Done(): c.session.Kill() return ctx.Err() case <-done: return err } } func (c *conn) ResetSession(ctx context.Context) error { c.session.Lock() defer c.session.Unlock() c.session.Reset() if c.session.IsBad() { return driver.ErrBadConn } return nil } func (c *conn) PrepareContext(ctx context.Context, query string) (stmt driver.Stmt, err error) { c.session.Lock() defer c.session.Unlock() if c.session.IsBad() { return nil, driver.ErrBadConn } if c.session.InQuery() { return nil, ErrNestedQuery } done := make(chan struct{}) go func() { var ( qd *p.QueryDescr pr *p.PrepareResult ) qd, err = p.NewQueryDescr(query, c.scanner) if err != nil { goto done } pr, err = c.session.Prepare(qd.Query()) if err != nil { goto done } if err = pr.Check(qd); err != nil { goto done } select { default: case <-ctx.Done(): return } stmt, err = newStmt(c.session, qd.Query(), qd.IsBulk(), pr) done: close(done) }() select { case <-ctx.Done(): c.session.Kill() return nil, ctx.Err() case <-done: return stmt, err } } func (c *conn) Close() error { c.session.Lock() defer c.session.Unlock() close(c.closed) // signal connection close return c.session.Close() } func (c *conn) BeginTx(ctx context.Context, opts driver.TxOptions) (tx driver.Tx, err error) { c.session.Lock() defer c.session.Unlock() if c.session.IsBad() { return nil, driver.ErrBadConn } if c.session.InTx() { return nil, ErrNestedTransaction } if c.session.InQuery() { return nil, ErrNestedQuery } level, ok := isolationLevel[opts.Isolation] if !ok { return nil, ErrUnsupportedIsolationLevel } done := make(chan struct{}) go func() { // set isolation level if _, err = c.session.ExecDirect(fmt.Sprintf(isolationLevelStmt, level)); err != nil { goto done } // set access mode if _, err = c.session.ExecDirect(fmt.Sprintf(accessModeStmt, readOnly[opts.ReadOnly])); err != nil { goto done } c.session.SetInTx(true) tx = newTx(c.session) done: close(done) }() select { case <-ctx.Done(): c.session.Kill() return nil, ctx.Err() case <-done: return tx, err } } func (c *conn) QueryContext(ctx context.Context, query string, args []driver.NamedValue) (rows driver.Rows, err error) { c.session.Lock() defer c.session.Unlock() if c.session.IsBad() { return nil, driver.ErrBadConn } if c.session.InQuery() { return nil, ErrNestedQuery } if len(args) != 0 { return nil, driver.ErrSkip //fast path not possible (prepare needed) } qd, err := p.NewQueryDescr(query, c.scanner) if err != nil { return nil, err } switch qd.Kind() { case p.QkCall: // direct execution of call procedure // - returns no parameter metadata (sps 82) but only field values // --> let's take the 'prepare way' for stored procedures return nil, driver.ErrSkip case p.QkID: // query call table result qrs, ok := p.QrsCache.Get(qd.ID()) if !ok { return nil, fmt.Errorf("invalid result set id %s", query) } return qrs, nil } sqltrace.Traceln(query) done := make(chan struct{}) go func() { rows, err = c.session.QueryDirect(query) close(done) }() select { case <-ctx.Done(): c.session.Kill() return nil, ctx.Err() case <-done: return rows, err } } func (c *conn) ExecContext(ctx context.Context, query string, args []driver.NamedValue) (r driver.Result, err error) { c.session.Lock() defer c.session.Unlock() if c.session.IsBad() { return nil, driver.ErrBadConn } if c.session.InQuery() { return nil, ErrNestedQuery } if len(args) != 0 { return nil, driver.ErrSkip //fast path not possible (prepare needed) } sqltrace.Traceln(query) done := make(chan struct{}) go func() { var qd *p.QueryDescr qd, err = p.NewQueryDescr(query, c.scanner) if err != nil { goto done } r, err = c.session.ExecDirect(qd.Query()) done: close(done) }() select { case <-ctx.Done(): c.session.Kill() return nil, ctx.Err() case <-done: return r, err } } // CheckNamedValue implements NamedValueChecker interface. // - called by sql driver for ExecContext and QueryContext // - no check needs to be performed as ExecContext and QueryContext provided // with parameters will force the 'prepare way' (driver.ErrSkip) // - Anyway, CheckNamedValue must be implemented to avoid default sql driver checks // which would fail for custom arg types like Lob func (c *conn) CheckNamedValue(nv *driver.NamedValue) error { 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 { t.session.Lock() defer t.session.Unlock() if t.session.IsBad() { return driver.ErrBadConn } return t.session.Commit() } func (t *tx) Rollback() error { t.session.Lock() defer t.session.Unlock() if t.session.IsBad() { return driver.ErrBadConn } return t.session.Rollback() } //statement // 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 { pr *p.PrepareResult session *p.Session query string bulk, flush bool maxBulkNum, bulkNum int args []driver.NamedValue } func newStmt(session *p.Session, query string, bulk bool, pr *p.PrepareResult) (*stmt, error) { return &stmt{session: session, query: query, pr: pr, bulk: bulk, maxBulkNum: session.MaxBulkNum()}, nil } func (s *stmt) Close() error { s.session.Lock() defer s.session.Unlock() if len(s.args) != 0 { sqltrace.Tracef("close: %s - not flushed records: %d)", s.query, len(s.args)/s.NumInput()) } return s.session.DropStatementID(s.pr.StmtID()) } func (s *stmt) NumInput() int { /* NumInput differs dependent on statement (check is done in QueryContext and ExecContext): - #args == #param (only in params): query, exec, exec bulk (non control query) - #args == #param (in and out params): exec call - #args == 0: exec bulk (control query) - #args == #input param: query call */ return -1 } func (s *stmt) QueryContext(ctx context.Context, args []driver.NamedValue) (rows driver.Rows, err error) { s.session.Lock() defer s.session.Unlock() if s.session.IsBad() { return nil, driver.ErrBadConn } if s.session.InQuery() { return nil, ErrNestedQuery } sqltrace.Tracef("%s %v", s.query, args) numArg := len(args) var numExpected int if s.pr.IsProcedureCall() { numExpected = s.pr.NumInputField() // input fields only } else { numExpected = s.pr.NumField() // all fields needs to be input fields } if numArg != numExpected { return nil, fmt.Errorf("invalid number of arguments %d - %d expected", numArg, numExpected) } done := make(chan struct{}) go func() { if s.pr.IsProcedureCall() { rows, err = s.session.QueryCall(s.pr, args) } else { rows, err = s.session.Query(s.pr, args) } close(done) }() select { case <-ctx.Done(): s.session.Kill() return nil, ctx.Err() case <-done: return rows, err } } func (s *stmt) ExecContext(ctx context.Context, args []driver.NamedValue) (r driver.Result, err error) { s.session.Lock() defer s.session.Unlock() if s.session.IsBad() { return nil, driver.ErrBadConn } if s.session.InQuery() { return nil, ErrNestedQuery } sqltrace.Tracef("%s %v", s.query, args) numArg := len(args) var numExpected int if s.bulk && numArg == 0 { // ok - bulk control numExpected = 0 } else { numExpected = s.pr.NumField() } if numArg != numExpected { return nil, fmt.Errorf("invalid number of arguments %d - %d expected", numArg, numExpected) } if numArg == 0 { // flush s.flush = true } defer func() { s.flush = false }() done := make(chan struct{}) go func() { switch { case s.pr.IsProcedureCall(): r, err = s.session.ExecCall(s.pr, args) case s.bulk: r, err = driver.ResultNoRows, nil if numArg != 0 { // add to argument buffer if s.args == nil { s.args = make([]driver.NamedValue, 0, DefaultBulkSize) } s.args = append(s.args, args...) s.bulkNum++ } if s.bulkNum != 0 && (s.flush || s.bulkNum == s.maxBulkNum) { // flush r, err = s.session.Exec(s.pr, s.args) s.args = s.args[:0] s.bulkNum = 0 } default: r, err = s.session.Exec(s.pr, args) } close(done) }() select { case <-ctx.Done(): s.session.Kill() return nil, ctx.Err() case <-done: return r, err } } // 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 = true return driver.ErrRemoveArgument case &flushTok: s.flush = true return driver.ErrRemoveArgument } } } return convertNamedValue(s.pr, nv) } golang-github-sap-go-hdb-0.100.10/driver/connector.go000066400000000000000000000317621370256154600222350ustar00rootroot00000000000000/* 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" "errors" "fmt" "io/ioutil" "net/url" "strconv" "sync" "time" "github.com/SAP/go-hdb/driver/dial" p "github.com/SAP/go-hdb/internal/protocol" ) // Data Format Version values. // Driver does currently support DfvLevel1, DfvLevel4 and DfvLevel6. const ( DfvLevel0 = 0 // base data format DfvLevel1 = 1 // eval types support all data types DfvLevel2 = 2 // reserved, broken, do not use DfvLevel3 = 3 // additional types Longdate, Secondate, Daydate, Secondtime supported for NGAP DfvLevel4 = 4 // generic support for new date/time types DfvLevel5 = 5 // spatial types in ODBC on request DfvLevel6 = 6 // BINTEXT DfvLevel7 = 7 // with boolean support DfvLevel8 = 8 // with FIXED8/12/16 support ) // var supportedDfvs = map[int]bool{DfvLevel1: true, DfvLevel4: true, DfvLevel6: true, DfvLevel8: true} var supportedDfvs = map[int]bool{DfvLevel1: true, DfvLevel4: true, DfvLevel6: true} // Connector default values. const ( DefaultDfv = DfvLevel6 // Default data version format level. DefaultTimeout = 300 // Default value connection timeout (300 seconds = 5 minutes). DefaultTCPKeepAlive = 15 * time.Second // Default TCP keep-alive value (copied from net.dial.go) DefaultFetchSize = 128 // Default value fetchSize. DefaultBulkSize = 1000 // Default value bulkSize. DefaultLobChunkSize = 4096 // Default value lobChunkSize. DefaultLegacy = true // Default value legacy. ) // Connector minimal values. const ( minTimeout = 0 // Minimal timeout value. minFetchSize = 1 // Minimal fetchSize value. minBulkSize = 1 // Minimal bulkSize value. minLobChunkSize = 128 // Minimal lobChunkSize // TODO check maxLobChunkSize maxLobChunkSize = 1 << 14 // Maximal lobChunkSize ) // check if Connector implements session parameter interface. var _ p.SessionConfig = (*Connector)(nil) /* SessionVariables 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, bulkSize int lobChunkSize int32 timeout, dfv int pingInterval time.Duration tcpKeepAlive time.Duration // see net.Dialer tlsConfig *tls.Config sessionVariables SessionVariables defaultSchema Identifier legacy bool dialer dial.Dialer } func newConnector() *Connector { return &Connector{ fetchSize: DefaultFetchSize, bulkSize: DefaultBulkSize, lobChunkSize: DefaultLobChunkSize, timeout: DefaultTimeout, tcpKeepAlive: DefaultTCPKeepAlive, dfv: DefaultDfv, legacy: DefaultLegacy, dialer: dial.DefaultDialer, } } // 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 } const parseDSNErrorText = "parse dsn error" // ParseDSNError is the error returned in case DSN is invalid. type ParseDSNError struct{ err error } func (e ParseDSNError) Error() string { if err := errors.Unwrap(e.err); err != nil { return fmt.Sprintf("%s: %s", parseDSNErrorText, err.Error()) } return parseDSNErrorText } // Unwrap returns the nested error. func (e ParseDSNError) Unwrap() error { return e.err } // NewDSNConnector creates a connector from a data source name. func NewDSNConnector(dsn string) (*Connector, error) { c := newConnector() u, err := url.Parse(dsn) if err != nil { return nil, &ParseDSNError{err} } c.host = u.Host if u.User != nil { c.username = u.User.Username() c.password, _ = u.User.Password() } var certPool *x509.CertPool for k, v := range u.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() } // BufferSize returns the bufferSize of the connector. func (c *Connector) BufferSize() int { c.mu.RLock(); defer c.mu.RUnlock(); return c.bufferSize } // 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 } // BulkSize returns the bulkSize of the connector. func (c *Connector) BulkSize() int { c.mu.RLock(); defer c.mu.RUnlock(); return c.bulkSize } /* SetBulkSize sets the bulkSize of the connector. */ func (c *Connector) SetBulkSize(bulkSize int) error { c.mu.Lock() defer c.mu.Unlock() if bulkSize < minBulkSize { bulkSize = minBulkSize } c.bulkSize = bulkSize return nil } // LobChunkSize returns the lobChunkSize of the connector. func (c *Connector) LobChunkSize() int32 { c.mu.RLock(); defer c.mu.RUnlock(); return c.lobChunkSize } // Dialer returns the dialer object of the connector. func (c *Connector) Dialer() dial.Dialer { c.mu.RLock(); defer c.mu.RUnlock(); return c.dialer } // SetDialer sets the dialer object of the connector. func (c *Connector) SetDialer(dialer dial.Dialer) error { c.mu.Lock() defer c.mu.Unlock() if dialer == nil { dialer = dial.DefaultDialer } c.dialer = dialer return nil } // Timeout returns the timeout of the connector. func (c *Connector) Timeout() int { c.mu.RLock(); defer c.mu.RUnlock(); return c.timeout } // TimeoutDuration returns the timeout of the connector as a time.Duration value. func (c *Connector) TimeoutDuration() time.Duration { return time.Duration(c.Timeout()) * time.Second } /* 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 } // PingInterval returns the connection ping interval of the connector. func (c *Connector) PingInterval() time.Duration { c.mu.RLock() defer c.mu.RUnlock() return c.pingInterval } /* SetPingInterval sets the connection ping interval value of the connector. If the ping interval is greater than zero, the driver pings all open connections (active or idle in connection pool) periodically. Parameter d defines the time between the pings. */ func (c *Connector) SetPingInterval(d time.Duration) error { c.mu.Lock() defer c.mu.Unlock() c.pingInterval = d return nil } // TCPKeepAlive returns the tcp keep-alive value of the connector. func (c *Connector) TCPKeepAlive() time.Duration { c.mu.RLock() defer c.mu.RUnlock() return c.tcpKeepAlive } /* SetTCPKeepAlive sets the tcp keep-alive value of the connector. For more information please see net.Dialer structure. */ func (c *Connector) SetTCPKeepAlive(tcpKeepAlive time.Duration) error { c.mu.Lock() defer c.mu.Unlock() c.tcpKeepAlive = tcpKeepAlive return nil } // Dfv returns the client data format version of the connector. func (c *Connector) Dfv() int { c.mu.RLock(); defer c.mu.RUnlock(); return c.dfv } // SetDfv sets the client data format version of the connector. func (c *Connector) SetDfv(dfv int) error { c.mu.Lock() defer c.mu.Unlock() if _, ok := supportedDfvs[dfv]; ok { c.dfv = dfv } else { c.dfv = DefaultDfv } 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 = make(SessionVariables, len(sessionVariables)) for k, v := range sessionVariables { c.sessionVariables[k] = v } return nil } // DefaultSchema returns the database default schema of the connector. func (c *Connector) DefaultSchema() Identifier { c.mu.RLock() defer c.mu.RUnlock() return c.defaultSchema } // SetDefaultSchema sets the database default schema of the connector. func (c *Connector) SetDefaultSchema(schema Identifier) error { c.mu.Lock() defer c.mu.Unlock() c.defaultSchema = schema return nil } // Legacy returns the connector legacy flag. func (c *Connector) Legacy() bool { c.mu.RLock() defer c.mu.RUnlock() return c.legacy } // SetLegacy sets the connector legacy flag. func (c *Connector) SetLegacy(b bool) error { c.mu.Lock() defer c.mu.Unlock() c.legacy = b 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.100.10/driver/connector_test.go000066400000000000000000000043341370256154600232670ustar00rootroot00000000000000/* 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(connector driver.Connector, t *testing.T) { 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(connector driver.Connector, sv goHdbDriver.SessionVariables, t *testing.T) { // check session variables db := sql.OpenDB(connector) 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) } } } func TestConnector(t *testing.T) { dsnConnector, err := goHdbDriver.NewDSNConnector(goHdbDriver.TestDSN) if err != nil { t.Fatal(err) } t.Run("dsnConnector", func(t *testing.T) { testConnector(dsnConnector, t) }) basicAuthConnector := goHdbDriver.NewBasicAuthConnector(dsnConnector.Host(), dsnConnector.Username(), dsnConnector.Password()) t.Run("basicAuthConnector", func(t *testing.T) { testConnector(basicAuthConnector, t) }) // set session variables sv := goHdbDriver.SessionVariables{"k1": "v1", "k2": "v2", "k3": "v3"} if err := dsnConnector.SetSessionVariables(sv); err != nil { t.Fatal(err) } t.Run("sessionVariables", func(t *testing.T) { testSessionVariables(dsnConnector, sv, t) }) } golang-github-sap-go-hdb-0.100.10/driver/convert.go000066400000000000000000000037021370256154600217140ustar00rootroot00000000000000/* Copyright 2014 SAP SE Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package driver import ( "database/sql" "database/sql/driver" "fmt" "io" "reflect" p "github.com/SAP/go-hdb/internal/protocol" ) func convertNamedValue(pr *p.PrepareResult, nv *driver.NamedValue) error { idx := nv.Ordinal - 1 f := pr.PrmField(idx) converter := f.Converter() v, out := normNamedValue(nv) if out != f.Out() { return fmt.Errorf("parameter descr / value mismatch - descr out %t value out %t", f.Out(), out) } if out && reflect.ValueOf(v).Kind() != reflect.Ptr { return fmt.Errorf("out parameter %v needs to be pointer variable", v) } var err error // let fields with own Value converter convert themselves first (e.g. NullInt64, ...) if valuer, ok := v.(driver.Valuer); ok { if v, err = valuer.Value(); err != nil { return err } } // special cases switch v := v.(type) { case io.Reader: if f.Out() { return fmt.Errorf("out parameter not writeable: %v", v) } case io.Writer: if f.In() { return fmt.Errorf("in parameter not readable: %v", v) } } if out { _, err = converter.Convert(v) // check field only } else { v, err = converter.Convert(v) // convert field } if err != nil { return err } nv.Value = v return nil } func normNamedValue(nv *driver.NamedValue) (interface{}, bool) { if out, isOut := nv.Value.(sql.Out); isOut { // out parameter return out.Dest, true // 'flatten' driver.NamedValue (remove sql.Out) } return nv.Value, false } golang-github-sap-go-hdb-0.100.10/driver/datatype_test.go000066400000000000000000000453641370256154600231200ustar00rootroot00000000000000/* 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" "io" "io/ioutil" "log" "math" "math/big" "os" "path/filepath" "reflect" "strconv" "sync" "testing" "time" "unicode/utf8" ) func testDataType(db *sql.DB, dataType string, fieldSize int, check func(in, out interface{}, fieldSize int, t *testing.T) bool, testData []interface{}, t *testing.T) { table := RandomIdentifier(fmt.Sprintf("%s_", dataType)) if fieldSize == 0 { if _, err := db.Exec(fmt.Sprintf("create table %s (x %s, i integer)", table, dataType)); err != nil { t.Fatal(err) } } else { if _, err := db.Exec(fmt.Sprintf("create table %s (x %s(%d), i integer)", table, dataType, fieldSize)); 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 values(?, ?)", 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(in, i); err != nil { t.Fatal(err) } } if err := tx.Commit(); err != nil { t.Fatal(err) } rows, err := db.Query(fmt.Sprintf("select * from %s order by i", table)) if err != nil { t.Fatal(err) } defer rows.Close() i := 0 for rows.Next() { in := testData[i] outRef := reflect.New(reflect.TypeOf(in)).Interface() switch outRef := outRef.(type) { case *NullDecimal: outRef.Decimal = (*Decimal)(new(big.Rat)) case *Lob: outRef.SetWriter(new(bytes.Buffer)) case *NullLob: outRef.Lob = new(Lob).SetWriter(new(bytes.Buffer)) } if err := rows.Scan(outRef, &i); err != nil { log.Fatal(err) } outVal := reflect.ValueOf(outRef).Elem().Interface() if !check(in, outVal, fieldSize, t) { t.Fatalf("%d value %v - expected %v", i, outVal, in) } i++ } if err := rows.Err(); err != nil { log.Fatal(err) } if i != len(testData) { t.Fatalf("rows %d - expected %d", i, len(testData)) } } func TestDataType(t *testing.T) { type testLobFile struct { content []byte isASCII bool } testLobFiles := make([]*testLobFile, 0) var initLobFilesOnce sync.Once testInitLobFiles := func(t *testing.T) { initLobFilesOnce.Do(func() { isASCII := func(content []byte) bool { for _, b := range content { if b >= utf8.RuneSelf { return false } } return true } 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()) { t.Logf("filenmane %s", 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) }) } 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 ) var tinyintTestData = []interface{}{ uint8(minTinyint), uint8(maxTinyint), sql.NullInt64{Valid: false, Int64: minTinyint}, sql.NullInt64{Valid: true, Int64: maxTinyint}, } var smallintTestData = []interface{}{ int16(minSmallint), int16(maxSmallint), sql.NullInt64{Valid: false, Int64: minSmallint}, sql.NullInt64{Valid: true, Int64: maxSmallint}, } var integerTestData = []interface{}{ int32(minInteger), int32(maxInteger), sql.NullInt64{Valid: false, Int64: minInteger}, sql.NullInt64{Valid: true, Int64: maxInteger}, } var bigintTestData = []interface{}{ int64(minBigint), int64(maxBigint), sql.NullInt64{Valid: false, Int64: minBigint}, sql.NullInt64{Valid: true, Int64: maxBigint}, } var realTestData = []interface{}{ float32(-maxReal), float32(maxReal), sql.NullFloat64{Valid: false, Float64: -maxReal}, sql.NullFloat64{Valid: true, Float64: maxReal}, } var doubleTestData = []interface{}{ float64(-maxDouble), float64(maxDouble), sql.NullFloat64{Valid: false, Float64: -maxDouble}, sql.NullFloat64{Valid: true, Float64: maxDouble}, } var asciiStringTestData = []interface{}{ "Hello HDB", "aaaaaaaaaa", sql.NullString{Valid: false, String: "Hello HDB"}, sql.NullString{Valid: true, String: "Hello HDB"}, } var stringTestData = []interface{}{ "Hello HDB", // varchar: UTF-8 4 bytes per char -> 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"}, } var binaryTestData = []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")}, } var alphanumTestData = []interface{}{ "0123456789", "1234567890", "abc", "123", "-abc", "-123", "0a1b2c", "12345678901234567890", sql.NullString{Valid: false, String: "42"}, sql.NullString{Valid: true, String: "42"}, } var timeTestData = []interface{}{ time.Now(), time.Date(2000, 12, 31, 23, 59, 59, 999999999, time.UTC), sql.NullTime{Valid: false, Time: time.Now()}, sql.NullTime{Valid: true, Time: time.Now()}, } var decimalTestData = []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)), (*Decimal)(big.NewRat(15, 1)), (*Decimal)(big.NewRat(4, 5)), (*Decimal)(big.NewRat(34, 10)), NullDecimal{Valid: false, Decimal: (*Decimal)(big.NewRat(1, 1))}, NullDecimal{Valid: true, Decimal: (*Decimal)(big.NewRat(1, 1))}, } var booleanTestData = []interface{}{ true, false, sql.NullBool{Valid: false, Bool: true}, sql.NullBool{Valid: true, Bool: false}, } checkInt := func(in, out interface{}, fieldSize int, t *testing.T) bool { if out, ok := out.(sql.NullInt64); ok { in := in.(sql.NullInt64) return in.Valid == out.Valid && (!in.Valid || in.Int64 == out.Int64) } return in == out } checkFloat := func(in, out interface{}, fieldSize int, t *testing.T) bool { if out, ok := out.(sql.NullFloat64); ok { in := in.(sql.NullFloat64) return in.Valid == out.Valid && (!in.Valid || in.Float64 == out.Float64) } return in == out } compareStringFixSize := func(in, out string) bool { if in != out[:len(in)] { return false } for _, r := range out[len(in):] { if r != rune(' ') { return false } } return true } checkFixString := func(in, out interface{}, fieldSize int, t *testing.T) bool { if out, ok := out.(sql.NullString); ok { in := in.(sql.NullString) return in.Valid == out.Valid && (!in.Valid || compareStringFixSize(in.String, out.String)) } return compareStringFixSize(in.(string), out.(string)) } checkString := func(in, out interface{}, fieldSize int, t *testing.T) bool { if out, ok := out.(sql.NullString); ok { in := in.(sql.NullString) return in.Valid == out.Valid && (!in.Valid || in.String == out.String) } return in == out } compareBytesFixSize := func(in, out []byte) bool { if !bytes.Equal(in, out[:len(in)]) { return false } for _, r := range out[len(in):] { if r != 0 { return false } } return true } checkFixBytes := func(in, out interface{}, fieldSize int, t *testing.T) bool { if out, ok := out.(NullBytes); ok { in := in.(NullBytes) return in.Valid == out.Valid && (!in.Valid || compareBytesFixSize(in.Bytes, out.Bytes)) } return compareBytesFixSize(in.([]byte), out.([]byte)) } checkBytes := func(in, out interface{}, fieldSize int, t *testing.T) bool { if out, ok := out.(NullBytes); ok { in := in.(NullBytes) return in.Valid == out.Valid && (!in.Valid || bytes.Equal(in.Bytes, out.Bytes)) } return bytes.Equal(in.([]byte), out.([]byte)) } equalDate := func(t1, t2 time.Time) bool { return t1.Year() == t2.Year() && t1.Month() == t2.Month() && t1.Day() == t2.Day() } equalTime := func(t1, t2 time.Time) bool { return t1.Hour() == t2.Hour() && t1.Minute() == t2.Minute() && t1.Second() == t2.Second() } equalDateTime := func(t1, t2 time.Time) bool { return equalDate(t1, t2) && equalTime(t1, t2) } equalMillisecond := func(t1, t2 time.Time) bool { return t1.Nanosecond()/1000000*1000000 == t2.Nanosecond()/1000000*1000000 } equalTimestamp := func(t1, t2 time.Time) bool { return equalDate(t1, t2) && equalTime(t1, t2) && equalMillisecond(t1, t2) } equalLongdate := func(t1, t2 time.Time) bool { //HDB: nanosecond 7-digit precision return equalDate(t1, t2) && equalTime(t1, t2) && (t1.Nanosecond()/100) == (t2.Nanosecond()/100) } checkDate := func(in, out interface{}, fieldSize int, t *testing.T) bool { if out, ok := out.(sql.NullTime); ok { in := in.(sql.NullTime) return in.Valid == out.Valid && (!in.Valid || equalDate(in.Time.UTC(), out.Time)) } return equalDate(in.(time.Time).UTC(), out.(time.Time)) } checkTime := func(in, out interface{}, fieldSize int, t *testing.T) bool { if out, ok := out.(sql.NullTime); ok { in := in.(sql.NullTime) return in.Valid == out.Valid && (!in.Valid || equalTime(in.Time.UTC(), out.Time)) } return equalTime(in.(time.Time).UTC(), out.(time.Time)) } checkDateTime := func(in, out interface{}, fieldSize int, t *testing.T) bool { if out, ok := out.(sql.NullTime); ok { in := in.(sql.NullTime) return in.Valid == out.Valid && (!in.Valid || equalDateTime(in.Time.UTC(), out.Time)) } return equalDateTime(in.(time.Time).UTC(), out.(time.Time)) } checkTimestamp := func(in, out interface{}, fieldSize int, t *testing.T) bool { if out, ok := out.(sql.NullTime); ok { in := in.(sql.NullTime) return in.Valid == out.Valid && (!in.Valid || equalTimestamp(in.Time.UTC(), out.Time)) } return equalTimestamp(in.(time.Time).UTC(), out.(time.Time)) } checkLongdate := func(in, out interface{}, fieldSize int, t *testing.T) bool { if out, ok := out.(sql.NullTime); ok { in := in.(sql.NullTime) return in.Valid == out.Valid && (!in.Valid || equalLongdate(in.Time.UTC(), out.Time)) } return equalLongdate(in.(time.Time).UTC(), out.(time.Time)) } logDecimal := func(in, out *big.Rat, t *testing.T) { t.Logf("In(num %s denum %s) - Out(num %s denum %s)", in.Num().String(), in.Denom().String(), out.Num().String(), out.Denom().String()) } checkDecimal := func(in, out interface{}, fieldSize int, t *testing.T) bool { if out, ok := out.(NullDecimal); ok { in := in.(NullDecimal) return in.Valid == out.Valid && (!in.Valid || ((*big.Rat)(in.Decimal)).Cmp((*big.Rat)(out.Decimal)) == 0) } logDecimal((*big.Rat)(in.(*Decimal)), (*big.Rat)(out.(*Decimal)), t) return ((*big.Rat)(in.(*Decimal))).Cmp((*big.Rat)(out.(*Decimal))) == 0 } checkBoolean := func(in, out interface{}, fieldSize int, t *testing.T) bool { if out, ok := out.(sql.NullBool); ok { in := in.(sql.NullBool) return in.Valid == out.Valid && (!in.Valid || in.Bool == out.Bool) } return in == out } lobTestData := func(ascii bool) []interface{} { testInitLobFiles(t) testData := make([]interface{}, 0, len(testLobFiles)) first := true for _, f := range testLobFiles { if !ascii || f.isASCII { if first { testData = append(testData, NullLob{Valid: false, Lob: &Lob{rd: bytes.NewReader(f.content)}}) testData = append(testData, NullLob{Valid: true, Lob: &Lob{rd: bytes.NewReader(f.content)}}) first = false } testData = append(testData, Lob{rd: bytes.NewReader(f.content)}) } } return testData } compareLob := func(in, out Lob, t *testing.T) bool { in.rd.(*bytes.Reader).Seek(0, io.SeekStart) content, err := ioutil.ReadAll(in.rd) if err != nil { t.Fatal(err) return false } // t.Log("CONTENT1") // t.Logf("%s", content) // t.Log("CONTENT2") // t.Logf("%s", out.wr.(*bytes.Buffer).Bytes()) // t.Log() // t.Log("CONTENT1") // t.Logf("%v", content) // t.Log("CONTENT2") // t.Logf("%v", out.wr.(*bytes.Buffer).Bytes()) t.Logf("length %d %d", len(content), len(out.wr.(*bytes.Buffer).Bytes())) content2 := out.wr.(*bytes.Buffer).Bytes() for i, ch := range content { if i < len(content2) { if ch != content2[i] { // t.Logf("%s", content[i:]) // t.Logf("%s", content2[i:]) // t.Log() t.Logf("diff %d %v %v", i, ch, content2[i]) return true //panic("unequal") } } } equal := bytes.Equal(content, out.wr.(*bytes.Buffer).Bytes()) if equal { return equal } return true } checkLob := func(in, out interface{}, fieldSize int, t *testing.T) bool { if out, ok := out.(NullLob); ok { in := in.(NullLob) return in.Valid == out.Valid && (!in.Valid || compareLob(*in.Lob, *out.Lob, t)) } return compareLob(in.(Lob), out.(Lob), t) } // baseline: alphanum is varchar formatAlphanumVarchar := func(s string, fieldSize int) string { i, err := strconv.ParseUint(s, 10, 64) if err != nil { // non numeric return s } // numeric (pad with leading zeroes) return fmt.Sprintf("%0"+strconv.Itoa(fieldSize)+"d", i) } formatAlphanum := func(s string) string { i, err := strconv.ParseUint(s, 10, 64) if err != nil { // non numeric return s } // numeric (return number as string with no leading zeroes) return strconv.FormatUint(i, 10) } checkAlphanumVarchar := func(in, out interface{}, fieldSize int, t *testing.T) bool { if out, ok := out.(sql.NullString); ok { in := in.(sql.NullString) return in.Valid == out.Valid && (!in.Valid || formatAlphanumVarchar(in.String, fieldSize) == out.String) } return formatAlphanumVarchar(in.(string), fieldSize) == out.(string) } checkAlphanum := func(in, out interface{}, fieldSize int, t *testing.T) bool { if out, ok := out.(sql.NullString); ok { in := in.(sql.NullString) return in.Valid == out.Valid && (!in.Valid || formatAlphanum(in.String) == out.String) } return formatAlphanum(in.(string)) == out.(string) } baselineTests := []struct { dataType string fieldSize int check func(in, out interface{}, fieldSize int, t *testing.T) bool testData []interface{} }{ {"timestamp", 0, checkTimestamp, timeTestData}, {"longdate", 0, checkTimestamp, timeTestData}, {"alphanum", 20, checkAlphanumVarchar, alphanumTestData}, } nonBaselineTests := []struct { dataType string fieldSize int check func(in, out interface{}, fieldSize int, t *testing.T) bool testData []interface{} }{ {"timestamp", 0, checkLongdate, timeTestData}, {"longdate", 0, checkLongdate, timeTestData}, {"alphanum", 20, checkAlphanum, alphanumTestData}, } commonTests := []struct { dataType string fieldSize int check func(in, out interface{}, fieldSize int, t *testing.T) bool testData []interface{} }{ {"tinyInt", 0, checkInt, tinyintTestData}, {"smallInt", 0, checkInt, smallintTestData}, {"integer", 0, checkInt, integerTestData}, {"bigint", 0, checkInt, bigintTestData}, {"real", 0, checkFloat, realTestData}, {"double", 0, checkFloat, doubleTestData}, /* 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 */ {"char", 40, checkFixString, asciiStringTestData}, {"varchar", 40, checkString, stringTestData}, {"nchar", 20, checkFixString, stringTestData}, {"nvarchar", 20, checkString, stringTestData}, {"binary", 20, checkFixBytes, binaryTestData}, {"varbinary", 20, checkBytes, binaryTestData}, {"date", 0, checkDate, timeTestData}, {"time", 0, checkTime, timeTestData}, {"seconddate", 0, checkDateTime, timeTestData}, {"daydate", 0, checkDate, timeTestData}, {"secondtime", 0, checkTime, timeTestData}, {"decimal", 0, checkDecimal, decimalTestData}, {"boolean", 0, checkBoolean, booleanTestData}, {"clob", 0, checkLob, lobTestData(true)}, {"nclob", 0, checkLob, lobTestData(false)}, {"blob", 0, checkLob, lobTestData(false)}, } extendedTests := []struct { sinceDfv int dataType string fieldSize int check func(in, out interface{}, fieldSize int, t *testing.T) bool testData []interface{} }{ {DfvLevel4, "text", 0, checkLob, lobTestData(false)}, {DfvLevel6, "bintext", 0, checkLob, lobTestData(true)}, } var testSet map[int]bool if testing.Short() { testSet = map[int]bool{DefaultDfv: true} } else { testSet = supportedDfvs } connector, err := NewDSNConnector(TestDSN) if err != nil { t.Fatal(err) } connector.SetDefaultSchema(TestSchema) for dfv := range testSet { name := fmt.Sprintf("dfv %d", dfv) t.Run(name, func(t *testing.T) { connector.SetDfv(dfv) db := sql.OpenDB(connector) defer db.Close() // common test for _, test := range commonTests { t.Run(test.dataType, func(t *testing.T) { testDataType(db, test.dataType, test.fieldSize, test.check, test.testData, t) }) } switch dfv { case DfvLevel1: for _, test := range baselineTests { t.Run(test.dataType, func(t *testing.T) { testDataType(db, test.dataType, test.fieldSize, test.check, test.testData, t) }) } default: for _, test := range nonBaselineTests { t.Run(test.dataType, func(t *testing.T) { testDataType(db, test.dataType, test.fieldSize, test.check, test.testData, t) }) } } for _, test := range extendedTests { if dfv >= test.sinceDfv { t.Run(test.dataType, func(t *testing.T) { testDataType(db, test.dataType, test.fieldSize, test.check, test.testData, t) }) } } }) } } golang-github-sap-go-hdb-0.100.10/driver/decimal.go000066400000000000000000000171741370256154600216420ustar00rootroot00000000000000/* 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) // GO 1.14 (incompatible change in Rat.Denom()) // if v is not initialized v.Denom() returns no reference to v.b but new int instead // --> init v (via v.Set(v)) before store v.Denom() in q v.Set(v) 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.100.10/driver/decimal_test.go000066400000000000000000000117031370256154600226710ustar00rootroot00000000000000/* 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) } func testDigits10(t *testing.T) { testData := []struct { x *big.Int digits int }{ {new(big.Int).SetInt64(0), 1}, {new(big.Int).SetInt64(1), 1}, {new(big.Int).SetInt64(9), 1}, {new(big.Int).SetInt64(10), 2}, {new(big.Int).SetInt64(99), 2}, {new(big.Int).SetInt64(100), 3}, {new(big.Int).SetInt64(999), 3}, {new(big.Int).SetInt64(1000), 4}, {new(big.Int).SetInt64(9999), 4}, {new(big.Int).SetInt64(10000), 5}, {new(big.Int).SetInt64(99999), 5}, {new(big.Int).SetInt64(100000), 6}, {new(big.Int).SetInt64(999999), 6}, {new(big.Int).SetInt64(1000000), 7}, {new(big.Int).SetInt64(9999999), 7}, {new(big.Int).SetInt64(10000000), 8}, {new(big.Int).SetInt64(99999999), 8}, {new(big.Int).SetInt64(100000000), 9}, {new(big.Int).SetInt64(999999999), 9}, {new(big.Int).SetInt64(1000000000), 10}, {new(big.Int).SetInt64(9999999999), 10}, } for i, d := range testData { 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) } } } func testConvertRat(t *testing.T) { testData := []struct { // in x *big.Rat digits int minExp int maxExp int // out cmp *big.Int neg bool exp int df decFlags }{ {new(big.Rat).SetFrac64(0, 1), 3, -2, 2, new(big.Int).SetInt64(0), false, 0, 0}, //convert 0 {new(big.Rat).SetFrac64(1, 1), 3, -2, 2, new(big.Int).SetInt64(1), false, 0, 0}, //convert 1 {new(big.Rat).SetFrac64(1, 10), 3, -2, 2, new(big.Int).SetInt64(1), false, -1, 0}, //convert 1/10 {new(big.Rat).SetFrac64(1, 99), 3, -2, 2, new(big.Int).SetInt64(1), false, -2, dfNotExact}, //convert 1/99 {new(big.Rat).SetFrac64(1, 100), 3, -2, 2, new(big.Int).SetInt64(1), false, -2, 0}, //convert 1/100 {new(big.Rat).SetFrac64(1, 1000), 3, -2, 2, new(big.Int).SetInt64(1), false, -3, dfUnderflow}, //convert 1/1000 {new(big.Rat).SetFrac64(10, 1), 3, -2, 2, new(big.Int).SetInt64(1), false, 1, 0}, //convert 10 {new(big.Rat).SetFrac64(100, 1), 3, -2, 2, new(big.Int).SetInt64(1), false, 2, 0}, //convert 100 {new(big.Rat).SetFrac64(1000, 1), 3, -2, 2, new(big.Int).SetInt64(10), false, 2, 0}, //convert 1000 {new(big.Rat).SetFrac64(10000, 1), 3, -2, 2, new(big.Int).SetInt64(100), false, 2, 0}, //convert 10000 {new(big.Rat).SetFrac64(100000, 1), 3, -2, 2, new(big.Int).SetInt64(100), false, 3, dfOverflow}, //convert 100000 {new(big.Rat).SetFrac64(999999, 1), 3, -2, 2, new(big.Int).SetInt64(100), false, 4, dfNotExact | dfOverflow}, //convert 999999 {new(big.Rat).SetFrac64(99999, 1), 3, -2, 2, new(big.Int).SetInt64(100), false, 3, dfNotExact | dfOverflow}, //convert 99999 {new(big.Rat).SetFrac64(9999, 1), 3, -2, 2, new(big.Int).SetInt64(100), false, 2, dfNotExact}, //convert 9999 {new(big.Rat).SetFrac64(99950, 1), 3, -2, 2, new(big.Int).SetInt64(100), false, 3, dfNotExact | dfOverflow}, //convert 99950 {new(big.Rat).SetFrac64(99949, 1), 3, -2, 2, new(big.Int).SetInt64(999), false, 2, dfNotExact}, //convert 99949 {new(big.Rat).SetFrac64(1, 3), 5, -5, 5, new(big.Int).SetInt64(33333), false, -5, dfNotExact}, //convert 1/3 {new(big.Rat).SetFrac64(2, 3), 5, -5, 5, new(big.Int).SetInt64(66667), false, -5, dfNotExact}, //convert 2/3 {new(big.Rat).SetFrac64(11, 2), 5, -5, 5, new(big.Int).SetInt64(55), false, -1, 0}, //convert 11/2 } m := new(big.Int) for i := 0; i < 1; i++ { // use for performance tests for j, d := range testData { 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) } } } } func TestDecimal(t *testing.T) { tests := []struct { name string fct func(t *testing.T) }{ {"decimalInfo", testDecimalInfo}, {"digits10", testDigits10}, {"convertRat", testConvertRat}, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { test.fct(t) }) } } golang-github-sap-go-hdb-0.100.10/driver/deprecated.go000066400000000000000000000024761370256154600223430ustar00rootroot00000000000000/* Copyright 2020 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" ) // deprecated driver interface methods func (*conn) Prepare(query string) (driver.Stmt, error) { panic("deprecated") } func (*conn) Begin() (driver.Tx, error) { panic("deprecated") } func (*conn) Exec(query string, args []driver.Value) (driver.Result, error) { panic("deprecated") } func (*conn) Query(query string, args []driver.Value) (driver.Rows, error) { panic("deprecated") } func (*stmt) Exec(args []driver.Value) (driver.Result, error) { panic("deprecated") } func (*stmt) Query(args []driver.Value) (rows driver.Rows, err error) { panic("deprecated") } // replaced driver interface methods // sql.Stmt.ColumnConverter --> replaced by CheckNamedValue golang-github-sap-go-hdb-0.100.10/driver/dial/000077500000000000000000000000001370256154600206145ustar00rootroot00000000000000golang-github-sap-go-hdb-0.100.10/driver/dial/dialer.go000066400000000000000000000026441370256154600224110ustar00rootroot00000000000000/* Copyright 2020 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 dial import ( "context" "net" "time" ) // DialerOptions contains optional parameters that might be used by a Dialer. type DialerOptions struct { Timeout, TCPKeepAlive time.Duration } // The Dialer interface needs to be implemented by custom Dialers. A Dialer for providing a custom driver connection // to the database can be set in the driver.Connector object. type Dialer interface { DialContext(ctx context.Context, address string, options DialerOptions) (net.Conn, error) } // DefaultDialer is the default driver Dialer implementation. var DefaultDialer Dialer = &dialer{} // default dialer implementation type dialer struct{} func (d *dialer) DialContext(ctx context.Context, address string, options DialerOptions) (net.Conn, error) { dialer := net.Dialer{Timeout: options.Timeout, KeepAlive: options.TCPKeepAlive} return dialer.DialContext(ctx, "tcp", address) } golang-github-sap-go-hdb-0.100.10/driver/doc.go000066400000000000000000000012271370256154600210010ustar00rootroot00000000000000/* 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.100.10/driver/driver.go000066400000000000000000000025471370256154600215350ustar00rootroot00000000000000/* 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" ) // DriverVersion is the version number of the hdb driver. const DriverVersion = "0.100.10" // DriverName is the driver name to use with sql.Open for hdb databases. const DriverName = "hdb" var drv = &hdbDrv{} //nolint:gochecknoinits 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) } golang-github-sap-go-hdb-0.100.10/driver/driver_test.go000066400000000000000000000133471370256154600225740ustar00rootroot00000000000000/* 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 testConnection(db *sql.DB, t *testing.T) { 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 testPing(db *sql.DB, t *testing.T) { if err := db.Ping(); err != nil { t.Fatal(err) } if err := db.PingContext(context.Background()); err != nil { t.Fatal(err) } } func testInsertByQuery(db *sql.DB, t *testing.T) { table := RandomIdentifier("insertByQuery_") if _, err := db.Exec(fmt.Sprintf("create table %s (i integer)", table)); err != nil { t.Fatal(err) } // insert value via Query if err := db.QueryRow(fmt.Sprintf("insert into %s values (?)", table), 42).Scan(); err != sql.ErrNoRows { t.Fatal(err) } // check value var i int if err := db.QueryRow(fmt.Sprintf("select * from %s", table)).Scan(&i); err != nil { t.Fatal(err) } if i != 42 { t.Fatalf("value %d - expected %d", i, 42) } } func testHDBError(db *sql.DB, t *testing.T) { //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", 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(db *sql.DB, 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 () language SQLSCRIPT as begin exec 'create table %[2]s(id int)'; exec 'drop table %[2]s'; end ` procedure := RandomIdentifier("proc_") tableName := RandomIdentifier("table_") if _, err := db.Exec(fmt.Sprintf(procOut, procedure, tableName)); err != nil { // Create stored procedure. t.Fatal(err) } if _, err := db.Exec(fmt.Sprintf("call %s", procedure)); err != nil { t.Fatal(err) } } func testQueryAttributeAlias(db *sql.DB, t *testing.T) { table := RandomIdentifier("queryAttributeAlias_") if _, err := db.Exec(fmt.Sprintf("create table %s (i integer, j integer)", table)); err != nil { t.Fatal(err) } rows, err := db.Query(fmt.Sprintf("select i as x, j from %s", 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(db *sql.DB, t *testing.T) { const maxRows = 10 table := RandomIdentifier("rowsAffected_") if _, err := db.Exec(fmt.Sprintf("create table %s (i integer)", table)); err != nil { t.Fatal(err) } stmt, err := db.Prepare(fmt.Sprintf("insert into %s values(?)", 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 set i = %d where i <> %d", table, maxRows, maxRows)) if err != nil { t.Fatal(err) } checkAffectedRows(t, result, maxRows) } func testUpsert(db *sql.DB, t *testing.T) { table := RandomIdentifier("upsert_") if _, err := db.Exec(fmt.Sprintf("create table %s (key int primary key, val int)", table)); err != nil { t.Fatal(err) } result, err := db.Exec(fmt.Sprintf("upsert %s values (1, 1)", table)) if err != nil { t.Fatal(err) } checkAffectedRows(t, result, 1) result, err = db.Exec(fmt.Sprintf("upsert %s values (:1, :1) where key = :2", table), 2, 2) if err != nil { t.Fatal(err) } checkAffectedRows(t, result, 1) result, err = db.Exec(fmt.Sprintf("upsert %s values (?, ?) where key = ?", table), 1, 9, 1) if err != nil { t.Fatal(err) } checkAffectedRows(t, result, 1) result, err = db.Exec(fmt.Sprintf("upsert %s values (?, ?) with primary key", table), 1, 8) if err != nil { t.Fatal(err) } checkAffectedRows(t, result, 1) result, err = db.Exec(fmt.Sprintf("upsert %[1]s select key + ?, val from %[1]s", 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) } } func TestDriver(t *testing.T) { tests := []struct { name string fct func(db *sql.DB, t *testing.T) }{ {"connection", testConnection}, {"ping", testPing}, {"insertByQuery", testInsertByQuery}, {"hdbError", testHDBError}, {"hdbWarning", testHDBWarning}, {"queryAttributeAlias", testQueryAttributeAlias}, {"rowsAffected", testRowsAffected}, {"upsert", testUpsert}, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { test.fct(TestDB, t) }) } } golang-github-sap-go-hdb-0.100.10/driver/dsn.go000066400000000000000000000040421370256154600210160ustar00rootroot00000000000000/* 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 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.100.10/driver/error.go000066400000000000000000000031741370256154600213700ustar00rootroot00000000000000/* 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.100.10/driver/example_bulk_test.go000066400000000000000000000026701370256154600237460ustar00rootroot00000000000000// +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.100.10/driver/example_call_test.go000066400000000000000000000126651370256154600237310ustar00rootroot00000000000000/* 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! } /* ExampleCallTableOutLegacy creates a stored procedure with one table output parameter and executes it in legacy mode. Legacy mode: 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_callTableOutLegacy() { 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 } /* ExampleCallTableOut creates a stored procedure with one table output parameter and executes it making use of sql.Rows scan parameters (non-legacy mode - *please see connector.SetLegacy(false)). 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. 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 ` connector, err := NewDSNConnector(TestDSN) if err != nil { log.Fatal(err) } // *Switch to non-legacy mode. connector.SetLegacy(false) db := sql.OpenDB(connector) 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 tableRows sql.Rows // 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(&tableRows); err != nil { log.Fatal(err) } 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.100.10/driver/example_connector_test.go000066400000000000000000000016771370256154600250110ustar00rootroot00000000000000/* 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.100.10/driver/example_decimal_test.go000066400000000000000000000034521370256154600244060ustar00rootroot00000000000000/* 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.100.10/driver/example_dsn_test.go000066400000000000000000000021631370256154600235720ustar00rootroot00000000000000/* 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.100.10/driver/example_error_test.go000066400000000000000000000024721370256154600241420ustar00rootroot00000000000000/* 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" "errors" "fmt" "log" "github.com/SAP/go-hdb/driver" ) const ( errCodeInvalidTableName = 259 ) func ExampleError() { db, err := sql.Open(driver.DriverName, driver.TestDSN) if err != nil { log.Fatal(err) } defer db.Close() invalidTableName := driver.RandomIdentifier("table_") stmt, err := db.Query(fmt.Sprintf("select * from %s", invalidTableName)) if err == nil { defer stmt.Close() } var dbError driver.Error if err != nil { // Check if error is driver.Error. if errors.As(err, &dbError) { switch dbError.Code() { case errCodeInvalidTableName: fmt.Print("invalid table name") default: log.Fatalf("code %d text %s", dbError.Code(), dbError.Text()) } } } // output: invalid table name } golang-github-sap-go-hdb-0.100.10/driver/example_lob_test.go000066400000000000000000000124041370256154600235610ustar00rootroot00000000000000/* 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 ( "bufio" "bytes" "database/sql" "fmt" "io" "log" "os" "sync" "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) } } /* ExampleLobPipe: - inserts data read from a file into a database large object field - and retrieves the data afterwards An io.Pipe is used to insert and retrieve Lob data in chunks. */ func ExampleLob_pipe() { // Open test file. file, err := os.Open("example_lob_test.go") if err != nil { log.Fatal(err) } defer file.Close() // Open Test database. connector, err := driver.NewDSNConnector(driver.TestDSN) if err != nil { log.Fatal(err) } db := sql.OpenDB(connector) 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) } // Create table. table := driver.RandomIdentifier("fileLob") if _, err := tx.Exec(fmt.Sprintf("create table %s (file nclob)", table)); err != nil { log.Fatalf("create table failed: %s", err) } stmt, err := tx.Prepare(fmt.Sprintf("insert into %s values (?)", table)) if err != nil { log.Fatal(err) } lob := &driver.Lob{} // Lob field. pipeReader, pipeWriter := io.Pipe() // Create pipe for writing Lob. lob.SetReader(pipeReader) // Use PipeReader as reader for Lob. // Use sync.WaitGroup to wait for go-routines to be ended. wg := new(sync.WaitGroup) wg.Add(1) // Select statement. // Start sql insert in own go-routine. // The go-routine is going to be ended when the data write via the PipeWriter is finalized. go func() { if _, err := stmt.Exec(lob); err != nil { log.Fatal(err) } fmt.Println("exec finalized") wg.Done() }() // Read file line by line and write data to pipe. scanner := bufio.NewScanner(file) for scanner.Scan() { pipeWriter.Write(scanner.Bytes()) pipeWriter.Write([]byte{'\n'}) // Write nl which was stripped off by scanner. } if err := scanner.Err(); err != nil { log.Fatal(err) } // Close pipeWriter (end insert into db). pipeWriter.Close() // Wait until exec go-routine is ended. wg.Wait() stmt.Close() if err := tx.Commit(); err != nil { log.Fatal(err) } pipeReader, pipeWriter = io.Pipe() // Create pipe for reading Lob. lob.SetWriter(pipeWriter) // Use PipeWriter as writer for Lob. wg.Add(1) // Exec statement. // Start sql select in own go-routine. // The go-routine is going to be ended when the data read via the PipeReader is finalized. go func() { if err := db.QueryRow(fmt.Sprintf("select * from %s", table)).Scan(lob); err != nil { log.Fatal(err) } fmt.Println("scan finalized") wg.Done() }() // Read Lob line by line via bufio.Scanner. scanner = bufio.NewScanner(pipeReader) for scanner.Scan() { // Do something with scan result. } if err := scanner.Err(); err != nil { log.Fatal(err) } pipeReader.Close() // Wait until select go-routine is ended. wg.Wait() // output: exec finalized // scan finalized } golang-github-sap-go-hdb-0.100.10/driver/example_query_test.go000066400000000000000000000030001370256154600241420ustar00rootroot00000000000000/* 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.100.10/driver/example_test.go000066400000000000000000000016261370256154600227310ustar00rootroot00000000000000/* 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.100.10/driver/identifier.go000066400000000000000000000025151370256154600223570ustar00rootroot00000000000000/* 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.100.10/driver/identifier_test.go000066400000000000000000000021071370256154600234130ustar00rootroot00000000000000/* 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{ {"_", "_"}, {"_A", "_A"}, {"A#$_", "A#$_"}, {"1", `"1"`}, {"a", `"a"`}, {"$", `"$"`}, {"日本語", `"日本語"`}, {"testTransaction", `"testTransaction"`}, {"a.b.c", `"a.b.c"`}, {"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.100.10/driver/lob.go000066400000000000000000000055751370256154600210220ustar00rootroot00000000000000/* 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" "fmt" "io" p "github.com/SAP/go-hdb/internal/protocol" ) // A Lob is the driver representation of a database large object field. // A Lob object uses an io.Reader object as source for writing content to a database lob field. // A Lob object uses an io.Writer object as destination for reading content from a database lob field. // A Lob can be created by contructor method NewLob with io.Reader and io.Writer as parameters or // created by new, setting io.Reader and io.Writer by SetReader and SetWriter methods. type Lob struct { rd io.Reader wr io.Writer } // 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} } // Reader returns the io.Reader of the Lob. func (l Lob) Reader() io.Reader { return l.rd } // 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 } // Writer returns the io.Writer of the Lob. func (l Lob) Writer() io.Writer { return l.wr } // 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 } // Scan implements the database/sql/Scanner interface. func (l *Lob) Scan(src interface{}) error { if l.wr == nil { return fmt.Errorf("lob error: initial writer %[1]T %[1]v", l) } ws, ok := src.(p.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 } // Value implements the database/sql/Valuer interface. func (l NullLob) Value() (driver.Value, error) { if !l.Valid { return nil, nil } return l.Lob, nil } // 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.100.10/driver/lob_test.go000066400000000000000000000100621370256154600220440ustar00rootroot00000000000000/* 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" "bytes" "context" "crypto/rand" "database/sql" "fmt" "io" "sync" "testing" ) func testLobInsert(db *sql.DB, t *testing.T) { table := RandomIdentifier("lobInsert") if _, err := db.Exec(fmt.Sprintf("create table %s (i1 integer, b1 blob, i2 integer, b2 blob)", table)); err != nil { t.Fatalf("create table failed: %s", err) } stmt, err := db.Prepare(fmt.Sprintf("insert into %s values (?,?,?,?)", table)) if err != nil { t.Fatal(err) } defer stmt.Close() } type randReader struct{} func (randReader) Read(b []byte) (n int, err error) { return rand.Read(b) } func testLobPipe(db *sql.DB, t *testing.T) { const lobSize = 10000 table := RandomIdentifier("lobPipe") lrd := io.LimitReader(randReader{}, lobSize) wrBuf := &bytes.Buffer{} wrBuf.ReadFrom(lrd) cmpBuf := &bytes.Buffer{} // 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("create table %s (b blob)", table)); err != nil { t.Fatalf("create table failed: %s", err) } stmt, err := tx.Prepare(fmt.Sprintf("insert into %s values (?)", table)) if err != nil { t.Fatal(err) } lob := &Lob{} rd, wr := io.Pipe() lob.SetReader(rd) wg := new(sync.WaitGroup) wg.Add(1) go func() { defer wg.Done() if _, err := stmt.Exec(lob); err != nil { t.Fatal(err) } t.Log("exec finalized") }() mwr := io.MultiWriter(wr, cmpBuf) wrBuf.WriteTo(mwr) wr.Close() wg.Wait() stmt.Close() if err := tx.Commit(); err != nil { t.Fatal(err) } rd, wr = io.Pipe() lob.SetWriter(wr) wg.Add(1) go func() { defer wg.Done() if err := db.QueryRow(fmt.Sprintf("select * from %s", table)).Scan(lob); err != nil { t.Fatal(err) } t.Log("scan finalized") }() rdBuf := &bytes.Buffer{} rdBuf.ReadFrom(rd) wg.Wait() if !bytes.Equal(rdBuf.Bytes(), cmpBuf.Bytes()) { t.Fatalf("read buffer is not equal to write buffer") } } func testLobDelayedScan(db *sql.DB, t *testing.T) { const lobSize = 10000 table := RandomIdentifier("lobPipe") rd := io.LimitReader(randReader{}, lobSize) // 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("create table %s (b blob)", table)); err != nil { t.Fatalf("create table failed: %s", err) } stmt, err := tx.Prepare(fmt.Sprintf("insert into %s values (?)", table)) if err != nil { t.Fatal(err) } lob := &Lob{} lob.SetReader(rd) if _, err := stmt.Exec(lob); err != nil { t.Fatal(err) } stmt.Close() if err := tx.Commit(); err != nil { t.Fatal(err) } wr := &bytes.Buffer{} lob.SetWriter(wr) ctx := context.Background() conn, err := db.Conn(ctx) // guarantee that same connection is used if err != nil { t.Fatal(err) } row := conn.QueryRowContext(ctx, fmt.Sprintf("select * from %s", table)) err = conn.PingContext(ctx) if err != ErrNestedQuery { t.Fatalf("got error %s - expected %s", err, ErrNestedQuery) } if err := row.Scan(lob); err != nil { t.Fatal(err) } // if !bytes.Equal(rdBuf.Bytes(), cmpBuf.Bytes()) { // t.Fatalf("read buffer is not equal to write buffer") // } } func TestLob(t *testing.T) { tests := []struct { name string fct func(db *sql.DB, t *testing.T) }{ {"insert", testLobInsert}, {"pipe", testLobPipe}, {"delayedScan", testLobDelayedScan}, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { test.fct(TestDB, t) }) } } golang-github-sap-go-hdb-0.100.10/driver/main_test.go000066400000000000000000000134131370256154600222170ustar00rootroot00000000000000/* Copyright 2020 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" "errors" "flag" "fmt" "log" "os" "testing" "time" ) const ( /* support of environment variables to - set e.g. DSN via env variable and - e.g. execute tests via go test -v ./... */ envDSN = "GOHDBDSN" ) const ( TestGoHDBSchemaPrefix = "goHdbTest_" ) // flags 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 // TestDropAllSchema will drop all schemas with GoHDBTestSchemaPrefix prefix to clean-up all not yet deleted // test schemas created by go-hdb unit tests. TestDropAllSchemas bool // TestPingConn sets the connection ping interval in milliseconds. // If zero, the connection ping is deactivated. TestPingConn int64 ) func init() { // check env variables dsn, ok := os.LookupEnv(envDSN) if !ok { dsn = "hdb://user:password@host:port" } flag.StringVar(&TestDSN, "dsn", dsn, "database dsn") flag.BoolVar(&TestDropSchema, "dropSchema", true, "drop test schema if test ran successfully") flag.BoolVar(&TestDropAllSchemas, "dropAllSchemas", false, "drop all existing test schemas if test ran successfully") flag.Int64Var(&TestPingConn, "pingConn", 0, "sets the connection ping interval (if zero, the connection ping is deactivated)") } // globals var ( // TestSchema will be used as test schema name and created on the database by TestMain. // The schema name consists of the prefix "test_" and a random Identifier. TestSchema Identifier // TestDB is instantiated by TestMain and should be used by tests. // TestDB uses TestDSN to connect to database. // Each TestDB connection will set TestSchema as default database schema. TestDB *sql.DB // // TestRec enables test recording. // TestRec bool // // TestRecFname is the filename used for test recording. // TestRecFname string // // TestRpl enables replaying test recordings. // TestRpl bool // // TestRplFname is the filename used for replaying the test recording. // TestRplFname string ) func init() { TestSchema = RandomIdentifier(TestGoHDBSchemaPrefix) } func testExit(err error) { func() { prefix := "" for err != nil { log.Printf("%s%s", prefix, err.Error()) prefix += "." err = errors.Unwrap(err) } }() os.Exit(1) } func TestMain(m *testing.M) { log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile) if !flag.Parsed() { flag.Parse() } conn := testSetup() exitCode := m.Run() testTeardown(exitCode, conn) os.Exit(exitCode) } func testSetup() *sql.Conn { connector, err := NewDSNConnector(TestDSN) if err != nil { testExit(err) } connector.SetPingInterval(time.Duration(TestPingConn) * time.Millisecond) TestDB = sql.OpenDB(connector) //TestDB.SetMaxIdleConns(0) ctx := context.Background() // create schema in own connection (no reuse of conn as DefaultSchema is not set) conn, err := TestDB.Conn(ctx) if err != nil { testExit(err) } if _, err := conn.ExecContext(ctx, fmt.Sprintf("create schema %s", TestSchema)); err != nil { testExit(err) } log.Printf("created schema %s", TestSchema) // now: set TestSchema in connector, so that all further connections are going to use it connector.SetDefaultSchema(TestSchema) return conn } func testTeardown(exitCode int, conn *sql.Conn) { ctx := context.Background() //schema := string(TestSchema) + "'" numTables, numProcs := 0, 0 if err := conn.QueryRowContext(ctx, fmt.Sprintf("select count(*) from sys.tables where schema_name = '%s'", string(TestSchema))).Scan(&numTables); err != nil { testExit(err) } if err := conn.QueryRowContext(ctx, fmt.Sprintf("select count(*) from sys.procedures where schema_name = '%s'", string(TestSchema))).Scan(&numProcs); err != nil { testExit(err) } log.Printf("schema %s - #tables created: %d #procedures created: %d", TestSchema, numTables, numProcs) if exitCode == 0 { switch { case TestDropAllSchemas: dropAllSchemas(ctx, conn) case TestDropSchema: dropSchema(ctx, conn, TestSchema) } } } func dropAllSchemas(ctx context.Context, conn *sql.Conn) { schemas := make([]string, 0) rows, err := conn.QueryContext(ctx, fmt.Sprintf("select schema_name from sys.schemas where schema_name like '%s_%%'", TestGoHDBSchemaPrefix)) if err != nil { testExit(err) } var schema string for rows.Next() { if err := rows.Scan(&schema); err != nil { testExit(err) } // cannot delete schemas in select loop (SQL Error 150 - statement cancelled or snapshot timestamp already invalidated) // --> collect them and delete outside of select schemas = append(schemas, schema) // cannot drop schemas in loop ( } if err := rows.Err(); err != nil { testExit(err) } rows.Close() for _, schema := range schemas { dropSchema(ctx, conn, Identifier(schema)) } log.Printf("number of dropped schemas: %d", len(schemas)) } func dropSchema(ctx context.Context, conn *sql.Conn, schema Identifier) { if _, err := conn.ExecContext(ctx, fmt.Sprintf("drop schema %s cascade", schema)); err != nil { testExit(err) } log.Printf("dropped schema %s", schema) } golang-github-sap-go-hdb-0.100.10/driver/sqltrace/000077500000000000000000000000001370256154600215215ustar00rootroot00000000000000golang-github-sap-go-hdb-0.100.10/driver/sqltrace/doc.go000066400000000000000000000011661370256154600226210ustar00rootroot00000000000000/* 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.100.10/driver/sqltrace/example_test.go000066400000000000000000000013671370256154600245510ustar00rootroot00000000000000/* 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.100.10/driver/sqltrace/sqltrace.go000066400000000000000000000032461370256154600236730ustar00rootroot00000000000000/* 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() //nolint:gochecknoinits 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.100.10/driver/time.go000066400000000000000000000013421370256154600211700ustar00rootroot00000000000000/* 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" ) // NullTime represents an time.Time that may be null. // Deprecated: Please use database/sql NullTime instead. type NullTime = sql.NullTime golang-github-sap-go-hdb-0.100.10/driver/tx_test.go000066400000000000000000000064611370256154600217330ustar00rootroot00000000000000/* 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(db *sql.DB, t *testing.T) { table := RandomIdentifier("testTxCommit_") if _, err := db.Exec(fmt.Sprintf("create table %s (i tinyint)", table)); err != nil { t.Fatal(err) } tx1, err := db.Begin() if err != nil { t.Fatal(err) } tx2, err := db.Begin() if err != nil { t.Fatal(err) } defer tx2.Rollback() //insert record in transaction 1 if _, err := tx1.Exec(fmt.Sprintf("insert into %s values(42)", table)); err != nil { t.Fatal(err) } //count records in transaction 1 i := 0 if err := tx1.QueryRow(fmt.Sprintf("select count(*) from %s", 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", 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", 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(db *sql.DB, t *testing.T) { table := RandomIdentifier("testTxRollback_") if _, err := db.Exec(fmt.Sprintf("create table %s (i tinyint)", 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 values(42)", table)); err != nil { t.Fatal(err) } //count records i := 0 if err := tx.QueryRow(fmt.Sprintf("select count(*) from %s", 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", table)).Scan(&i); err != nil { t.Fatal(err) } if i != 0 { t.Fatal(fmt.Errorf("tx: invalid number of records %d - 0 expected", i)) } } func TestTransaction(t *testing.T) { tests := []struct { name string fct func(db *sql.DB, t *testing.T) }{ {"transactionCommit", testTransactionCommit}, {"transactionRollback", testTransactionRollback}, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { test.fct(TestDB, t) }) } } golang-github-sap-go-hdb-0.100.10/go.mod000066400000000000000000000002051370256154600175130ustar00rootroot00000000000000module github.com/SAP/go-hdb go 1.14 require ( golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37 golang.org/x/text v0.3.2 ) golang-github-sap-go-hdb-0.100.10/go.sum000066400000000000000000000017321370256154600175460ustar00rootroot00000000000000golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37 h1:cg5LA/zNPRzIXIWSCxQW10Rvpy94aQh3LT/ShoCpkHw= golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang-github-sap-go-hdb-0.100.10/internal/000077500000000000000000000000001370256154600202245ustar00rootroot00000000000000golang-github-sap-go-hdb-0.100.10/internal/protocol/000077500000000000000000000000001370256154600220655ustar00rootroot00000000000000golang-github-sap-go-hdb-0.100.10/internal/protocol/authentication.go000066400000000000000000000312001370256154600254270ustar00rootroot00000000000000/* Copyright 2020 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" "encoding/binary" "fmt" "math" "golang.org/x/crypto/pbkdf2" "github.com/SAP/go-hdb/internal/protocol/encoding" "github.com/SAP/go-hdb/internal/unicode/cesu8" ) const ( mnSCRAMSHA256 = "SCRAMSHA256" // password mnSCRAMPBKDF2SHA256 = "SCRAMPBKDF2SHA256" // pbkdf2 ) const ( clientChallengeSize = 64 serverChallengeSize = 48 saltSize = 16 clientProofSize = 32 ) const ( int16Size = 2 uint32Size = 4 ) type authStepper interface { next() (partReadWriter, error) } type _authShortCESU8String struct{} type _authShortBytes struct{} var authShortCESU8String = _authShortCESU8String{} var authShortBytes = _authShortBytes{} func (_authShortCESU8String) decode(dec *encoding.Decoder) string { size := dec.Byte() return string(dec.CESU8Bytes(int(size))) } func (_authShortCESU8String) encode(enc *encoding.Encoder, s string) error { size := cesu8.StringSize(s) if size > math.MaxUint8 { return fmt.Errorf("invalid auth parameter length %d", size) } enc.Byte(byte(size)) enc.CESU8String(s) return nil } func (_authShortBytes) decode(dec *encoding.Decoder) []byte { size := dec.Byte() b := make([]byte, size) dec.Bytes(b) return b } func (_authShortBytes) encode(enc *encoding.Encoder, b []byte) error { size := len(b) if size > math.MaxUint8 { return fmt.Errorf("invalid auth parameter length %d", size) } enc.Byte(byte(size)) enc.Bytes(b) return nil } type authMethod struct { method string clientChallenge []byte } func (m *authMethod) String() string { return fmt.Sprintf("method %s clientChallenge %v", m.method, m.clientChallenge) } func (m *authMethod) size() int { size := 2 // number of parameters size += len(m.method) size += len(m.clientChallenge) return size } func (m *authMethod) decode(dec *encoding.Decoder, ph *partHeader) error { m.method = string(authShortBytes.decode(dec)) m.clientChallenge = authShortBytes.decode(dec) return nil } func (m *authMethod) encode(enc *encoding.Encoder) error { if err := authShortBytes.encode(enc, []byte(m.method)); err != nil { return err } if err := authShortBytes.encode(enc, m.clientChallenge); err != nil { return err } return nil } type authInitReq struct { username string methods []*authMethod } func (r *authInitReq) String() string { return fmt.Sprintf("username %s methods %v", r.username, r.methods) } func (r *authInitReq) size() int { size := int16Size // no of parameters size++ // len byte username size += cesu8.StringSize(r.username) for _, m := range r.methods { size += m.size() } return size } func (r *authInitReq) decode(dec *encoding.Decoder, ph *partHeader) error { numPrm := int(dec.Int16()) r.username = authShortCESU8String.decode(dec) numMethod := (numPrm - 1) / 2 r.methods = make([]*authMethod, numMethod) for i := 0; i < len(r.methods); i++ { authMethod := &authMethod{} r.methods[i] = authMethod if err := authMethod.decode(dec, ph); err != nil { return err } } return nil } func (r *authInitReq) encode(enc *encoding.Encoder) error { enc.Int16(int16(1 + len(r.methods)*2)) // username + methods á each two fields if err := authShortCESU8String.encode(enc, r.username); err != nil { return err } for _, m := range r.methods { m.encode(enc) } return nil } type authInitSCRAMSHA256Rep struct { salt, serverChallenge []byte } func (r *authInitSCRAMSHA256Rep) String() string { return fmt.Sprintf("salt %v serverChallenge %v", r.salt, r.serverChallenge) } func (r *authInitSCRAMSHA256Rep) decode(dec *encoding.Decoder, ph *partHeader) error { numPrm := int(dec.Int16()) if numPrm != 2 { return fmt.Errorf("invalid number of parameters %d - expected %d", numPrm, 2) } r.salt = authShortBytes.decode(dec) r.serverChallenge = authShortBytes.decode(dec) return nil } type authInitSCRAMPBKDF2SHA256Rep struct { salt, serverChallenge []byte rounds uint32 } func (r *authInitSCRAMPBKDF2SHA256Rep) String() string { return fmt.Sprintf("salt %v serverChallenge %v rounds %d", r.salt, r.serverChallenge, r.rounds) } func (r *authInitSCRAMPBKDF2SHA256Rep) decode(dec *encoding.Decoder, ph *partHeader) error { numPrm := int(dec.Int16()) if numPrm != 3 { return fmt.Errorf("invalid number of parameters %d - expected %d", numPrm, 3) } r.salt = authShortBytes.decode(dec) r.serverChallenge = authShortBytes.decode(dec) size := dec.Byte() if size != uint32Size { return fmt.Errorf("invalid auth uint32 size %d - expected %d", size, uint32Size) } r.rounds = dec.Uint32ByteOrder(binary.BigEndian) // big endian coded (e.g. rounds param) return nil } type authInitRep struct { method string prms partDecoder } func (r *authInitRep) String() string { return fmt.Sprintf("method %s parameters %v", r.method, r.prms) } func (r *authInitRep) size() int { panic("not implemented") } func (r *authInitRep) encode(enc *encoding.Encoder) error { panic("not implemented") } func (r *authInitRep) decode(dec *encoding.Decoder, ph *partHeader) error { numPrm := int(dec.Int16()) if numPrm != 2 { return fmt.Errorf("invalid number of parameters %d - expected %d", numPrm, 2) } r.method = string(authShortBytes.decode(dec)) dec.Byte() // sub parameter length switch r.method { case mnSCRAMSHA256: r.prms = &authInitSCRAMSHA256Rep{} return r.prms.decode(dec, ph) case mnSCRAMPBKDF2SHA256: r.prms = &authInitSCRAMPBKDF2SHA256Rep{} return r.prms.decode(dec, ph) default: return fmt.Errorf("invalid or not supported authentication method %s", r.method) } } type authClientProofReq struct { clientProof []byte } func (r *authClientProofReq) String() string { return fmt.Sprintf("clientProof %v", r.clientProof) } func (r *authClientProofReq) size() int { size := int16Size // no of parameters size += len(r.clientProof) + 1 return size } func (r *authClientProofReq) decode(dec *encoding.Decoder, ph *partHeader) error { numPrm := int(dec.Int16()) if numPrm != 1 { return fmt.Errorf("invalid number of parameters %d - expected %d", numPrm, 1) } r.clientProof = authShortBytes.decode(dec) return nil } func (r *authClientProofReq) encode(enc *encoding.Encoder) error { enc.Int16(1) if err := authShortBytes.encode(enc, r.clientProof); err != nil { return err } return nil } type authFinalReq struct { username, method string prms partDecodeEncoder } func (r *authFinalReq) String() string { return fmt.Sprintf("username %s methods %s parameter %v", r.username, r.method, r.prms) } func (r *authFinalReq) size() int { size := int16Size // no of parameters size += cesu8.StringSize(r.username) + 1 size += len(r.method) + 1 size++ // len sub parameters size += r.prms.size() return size } func (r *authFinalReq) decode(dec *encoding.Decoder, ph *partHeader) error { numPrm := int(dec.Int16()) if numPrm != 3 { return fmt.Errorf("invalid number of parameters %d - expected %d", numPrm, 3) } r.username = authShortCESU8String.decode(dec) r.method = string(authShortBytes.decode(dec)) dec.Byte() // sub parameters r.prms = &authClientProofReq{} return r.prms.decode(dec, ph) } func (r *authFinalReq) encode(enc *encoding.Encoder) error { enc.Int16(3) if err := authShortCESU8String.encode(enc, r.username); err != nil { return err } if err := authShortBytes.encode(enc, []byte(r.method)); err != nil { return err } enc.Byte(byte(r.prms.size())) return r.prms.encode(enc) } type authServerProofRep struct { serverProof []byte } func (r *authServerProofRep) String() string { return fmt.Sprintf("serverProof %v", r.serverProof) } func (r *authServerProofRep) decode(dec *encoding.Decoder, ph *partHeader) error { numPrm := int(dec.Int16()) if numPrm != 1 { return fmt.Errorf("invalid number of server proof parameters %d - expected %d", numPrm, 1) } r.serverProof = authShortBytes.decode(dec) return nil } func (r *authServerProofRep) encode(enc *encoding.Encoder) error { enc.Int16(1) if err := authShortBytes.encode(enc, r.serverProof); err != nil { return err } return nil } type authFinalRep struct { method string prms partDecoder } func (r *authFinalRep) String() string { return fmt.Sprintf("method %s parameter %v", r.method, r.prms) } func (r *authFinalRep) size() int { panic("not implemented") } func (r *authFinalRep) encode(enc *encoding.Encoder) error { panic("not implemented") } func (r *authFinalRep) decode(dec *encoding.Decoder, ph *partHeader) error { numPrm := int(dec.Int16()) if numPrm != 2 { return fmt.Errorf("invalid number of parameters %d - expected %d", numPrm, 2) } r.method = string(authShortBytes.decode(dec)) if size := dec.Byte(); size == 0 { // sub parameter length // mnSCRAMSHA256: server does not return server proof parameter return nil } r.prms = &authServerProofRep{} return r.prms.decode(dec, ph) } type auth struct { step int username, password string methods []*authMethod initRep *authInitRep } func newAuth(username, password string) *auth { return &auth{ username: username, password: password, methods: []*authMethod{ {method: mnSCRAMPBKDF2SHA256, clientChallenge: clientChallenge()}, {method: mnSCRAMSHA256, clientChallenge: clientChallenge()}, }, initRep: &authInitRep{}, } } func (a *auth) clientChallenge(method string) []byte { for _, m := range a.methods { if m.method == method { return m.clientChallenge } } panic("should never happen") } func (a *auth) next() (partReadWriter, error) { defer func() { a.step++ }() switch a.step { case 0: for _, m := range a.methods { if len(m.clientChallenge) != clientChallengeSize { return nil, fmt.Errorf("invalid client challenge size %d - expected %d", len(m.clientChallenge), clientChallengeSize) } } return &authInitReq{username: a.username, methods: a.methods}, nil case 1: return a.initRep, nil case 2: var salt, serverChallenge, key []byte switch a.initRep.method { case mnSCRAMSHA256: prms := a.initRep.prms.(*authInitSCRAMSHA256Rep) salt, serverChallenge = prms.salt, prms.serverChallenge key = scramsha256Key([]byte(a.password), prms.salt) case mnSCRAMPBKDF2SHA256: prms := a.initRep.prms.(*authInitSCRAMPBKDF2SHA256Rep) salt, serverChallenge = prms.salt, prms.serverChallenge key = scrampbkdf2sha256Key([]byte(a.password), prms.salt, int(prms.rounds)) default: panic("should never happen") } if len(salt) != saltSize { return nil, fmt.Errorf("invalid salt size %d - expected %d", len(salt), saltSize) } if len(serverChallenge) != serverChallengeSize { return nil, fmt.Errorf("invalid server challenge size %d - expected %d", len(serverChallenge), serverChallengeSize) } clientProof := clientProof(key, []byte(a.password), salt, serverChallenge, a.clientChallenge(a.initRep.method)) if len(clientProof) != clientProofSize { return nil, fmt.Errorf("invalid client proof size %d - expected %d", len(clientProof), clientProofSize) } return &authFinalReq{username: a.username, method: a.initRep.method, prms: &authClientProofReq{clientProof: clientProof}}, nil case 3: return &authFinalRep{}, nil } panic("should never happen") } func clientChallenge() []byte { r := make([]byte, clientChallengeSize) if _, err := rand.Read(r); err != nil { plog.Fatalf("client challenge fatal error") } return r } func scramsha256Key(password, salt []byte) []byte { return _sha256(_hmac(password, salt)) } func scrampbkdf2sha256Key(password, salt []byte, rounds int) []byte { return _sha256(pbkdf2.Key(password, salt, int(rounds), clientProofSize, sha256.New)) } func clientProof(key, password, salt, serverChallenge, clientChallenge []byte) []byte { sig := _hmac(_sha256(key), salt, serverChallenge, clientChallenge) proof := xor(sig, key) return proof } func _sha256(p []byte) []byte { hash := sha256.New() hash.Write(p) s := hash.Sum(nil) return s } func _hmac(key []byte, prms ...[]byte) []byte { hash := hmac.New(sha256.New, key) for _, p := range prms { hash.Write(p) } s := hash.Sum(nil) 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.100.10/internal/protocol/authentication_test.go000066400000000000000000000066761370256154600265110ustar00rootroot00000000000000/* 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" ) func TestAuthentication(t *testing.T) { testData := []struct { method string salt []byte serverChallenge []byte rounds int clientChallenge []byte password []byte clientProof []byte serverProof []byte }{ { method: mnSCRAMSHA256, 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{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}, }, { method: mnSCRAMPBKDF2SHA256, salt: []byte{51, 178, 213, 213, 92, 82, 194, 40, 80, 120, 197, 91, 166, 67, 23, 63}, serverChallenge: []byte{32, 91, 165, 18, 158, 77, 134, 69, 128, 157, 69, 209, 47, 33, 171, 164, 56, 172, 229, 0, 153, 3, 65, 29, 239, 210, 186, 134, 81, 32, 29, 137, 239, 167, 39, 1, 171, 117, 85, 138, 109, 38, 42, 77, 43, 42, 82, 70}, rounds: 15000, clientChallenge: []byte{137, 156, 182, 60, 158, 138, 93, 103, 80, 202, 54, 191, 210, 78, 142, 207, 210, 176, 157, 129, 128, 19, 135, 0, 127, 26, 58, 197, 188, 216, 121, 26, 120, 196, 34, 138, 5, 8, 58, 32, 36, 240, 199, 126, 164, 112, 64, 35, 46, 102, 255, 249, 126, 250, 24, 103, 198, 152, 33, 75, 6, 179, 187, 230}, password: []byte{84, 111, 111, 114, 49, 50, 51, 52}, clientProof: []byte{253, 181, 101, 0, 214, 222, 25, 99, 98, 253, 141, 106, 38, 255, 16, 153, 34, 74, 211, 70, 21, 91, 71, 223, 170, 36, 249, 124, 1, 135, 176, 37}, serverProof: []byte{228, 2, 183, 82, 29, 218, 234, 242, 40, 50, 142, 158, 142, 153, 185, 189, 130, 51, 176, 155, 23, 179, 58, 19, 126, 144, 139, 229, 116, 3, 242, 197}, }, } for _, r := range testData { var key []byte switch r.method { case mnSCRAMSHA256: key = scramsha256Key(r.password, r.salt) case mnSCRAMPBKDF2SHA256: key = scrampbkdf2sha256Key(r.password, r.salt, r.rounds) default: t.Fatalf("unknown authentication method %s", r.method) } clientProof := clientProof(key, r.password, r.salt, r.serverChallenge, r.clientChallenge) 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.100.10/internal/protocol/bytes.go000066400000000000000000000015571370256154600235520ustar00rootroot00000000000000/* Copyright 2020 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 func sizeBuffer(b []byte, size int) []byte { if b == nil || size > cap(b) { return make([]byte, size) } return b[:size] } func resizeBuffer(b1 []byte, size int) []byte { if b1 == nil || cap(b1) < size { b2 := make([]byte, size) copy(b2, b1) // !!! keep content return b2 } return b1[:size] } golang-github-sap-go-hdb-0.100.10/internal/protocol/clientid.go000066400000000000000000000023271370256154600242130ustar00rootroot00000000000000/* 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/protocol/encoding" ) 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) String() string { return string(id) } func (id clientID) size() int { return len(id) } func (id *clientID) decode(dec *encoding.Decoder, ph *partHeader) error { *id = sizeBuffer(*id, int(ph.bufferLength)) dec.Bytes(*id) return dec.Error() } func (id clientID) encode(enc *encoding.Encoder) error { enc.Bytes(id); return nil } golang-github-sap-go-hdb-0.100.10/internal/protocol/command.go000066400000000000000000000021001370256154600240230ustar00rootroot00000000000000/* 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/protocol/encoding" "github.com/SAP/go-hdb/internal/unicode/cesu8" ) // cesu8 command type command []byte func (c command) String() string { return string(c) } func (c command) size() int { return cesu8.Size(c) } func (c *command) decode(dec *encoding.Decoder, ph *partHeader) error { *c = sizeBuffer(*c, int(ph.bufferLength)) *c = dec.CESU8Bytes(len(*c)) return dec.Error() } func (c command) encode(enc *encoding.Encoder) error { enc.CESU8Bytes(c); return nil } golang-github-sap-go-hdb-0.100.10/internal/protocol/connectoption.go000066400000000000000000000155241370256154600253050ustar00rootroot00000000000000/* 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 //nolint const ( coConnectionID connectOption = 1 coCompleteArrayExecution connectOption = 2 //!< @deprecated Array execution semantics, always true. coClientLocale connectOption = 3 //!< Client locale information. coSupportsLargeBulkOperations connectOption = 4 //!< Bulk operations >32K are supported. coDistributionEnabled connectOption = 5 //!< @deprecated Distribution (topology & call routing) enabled coPrimaryConnectionID connectOption = 6 //!< @deprecated Id of primary connection (unused). coPrimaryConnectionHost connectOption = 7 //!< @deprecated Primary connection host name (unused). coPrimaryConnectionPort connectOption = 8 //!< @deprecated Primary connection port (unused). coCompleteDatatypeSupport connectOption = 9 //!< @deprecated All data types supported (always on). coLargeNumberOfParametersSupport connectOption = 10 //!< Number of parameters >32K is supported. coSystemID connectOption = 11 //!< SID of SAP HANA Database system (output only). coDataFormatVersion connectOption = 12 //!< Version of data format used in communication (@see DataFormatVersionEnum). coAbapVarcharMode connectOption = 13 //!< ABAP varchar mode is enabled (trailing blanks in string constants are trimmed off). coSelectForUpdateSupported connectOption = 14 //!< SELECT FOR UPDATE function code understood by client coClientDistributionMode connectOption = 15 //!< client distribution mode coEngineDataFormatVersion connectOption = 16 //!< Engine version of data format used in communication (@see DataFormatVersionEnum). coDistributionProtocolVersion connectOption = 17 //!< version of distribution protocol handling (@see DistributionProtocolVersionEnum) coSplitBatchCommands connectOption = 18 //!< permit splitting of batch commands coUseTransactionFlagsOnly connectOption = 19 //!< use transaction flags only for controlling transaction coRowSlotImageParameter connectOption = 20 //!< row-slot image parameter passing coIgnoreUnknownParts connectOption = 21 //!< server does not abort on unknown parts coTableOutputParameterMetadataSupport connectOption = 22 //!< support table type output parameter metadata. coDataFormatVersion2 connectOption = 23 //!< Version of data format used in communication (as DataFormatVersion used wrongly in old servers) coItabParameter connectOption = 24 //!< bool option to signal abap itab parameter support coDescribeTableOutputParameter connectOption = 25 //!< override "omit table output parameter" setting in this session coColumnarResultSet connectOption = 26 //!< column wise result passing coScrollableResultSet connectOption = 27 //!< scrollable result set coClientInfoNullValueSupported connectOption = 28 //!< can handle null values in client info coAssociatedConnectionID connectOption = 29 //!< associated connection id coNonTransactionalPrepare connectOption = 30 //!< can handle and uses non-transactional prepare coFdaEnabled connectOption = 31 //!< Fast Data Access at all enabled coOSUser connectOption = 32 //!< client OS user name coRowSlotImageResultSet connectOption = 33 //!< row-slot image result passing coEndianness connectOption = 34 //!< endianness (@see EndiannessEnumType) coUpdateTopologyAnwhere connectOption = 35 //!< Allow update of topology from any reply coEnableArrayType connectOption = 36 //!< Enable supporting Array data type coImplicitLobStreaming connectOption = 37 //!< implicit lob streaming coCachedViewProperty connectOption = 38 //!< provide cached view timestamps to the client coXOpenXAProtocolSupported connectOption = 39 //!< JTA(X/Open XA) Protocol coMasterCommitRedirectionSupported connectOption = 40 //!< S2PC routing control coActiveActiveProtocolVersion connectOption = 41 //!< Version of Active/Active protocol coActiveActiveConnectionOriginSite connectOption = 42 //!< Tell where is the anchor connection located. This is unidirectional property from client to server. coQueryTimeoutSupported connectOption = 43 //!< support query timeout (e.g., Statement.setQueryTimeout) coFullVersionString connectOption = 44 //!< Full version string of the client or server (the sender) (added to hana2sp0) coDatabaseName connectOption = 45 //!< Database name (string) that we connected to (sent by server) (added to hana2sp0) coBuildPlatform connectOption = 46 //!< Build platform of the client or server (the sender) (added to hana2sp0) coImplicitXASessionSupported connectOption = 47 //!< S2PC routing control - implicit XA join support after prepare and before execute in MessageType_Prepare, MessageType_Execute and MessageType_PrepareAndExecute coClientSideColumnEncryptionVersion connectOption = 48 //!< Version of client-side column encryption coCompressionLevelAndFlags connectOption = 49 //!< Network compression level and flags (added to hana2sp02) coClientSideReExecutionSupported connectOption = 50 //!< support client-side re-execution for client-side encryption (added to hana2sp03) coClientReconnectWaitTimeout connectOption = 51 //!< client reconnection wait timeout for transparent session recovery coOriginalAnchorConnectionID connectOption = 52 //!< original anchor connectionID to notify client's RECONNECT coFlagSet1 connectOption = 53 //!< flags for aggregating several options coTopologyNetworkGroup connectOption = 54 //!< NetworkGroup name sent by client to choose topology mapping (added to hana2sp04) coIPAddress connectOption = 55 //!< IP Address of the sender (added to hana2sp04) coLRRPingTime connectOption = 56 //!< Long running request ping time ) golang-github-sap-go-hdb-0.100.10/internal/protocol/connectoption_string.go000066400000000000000000000077511370256154600266760ustar00rootroot00000000000000// Code generated by "stringer -type=connectOption"; DO NOT EDIT. package protocol import "strconv" func _() { // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. var x [1]struct{} _ = x[coConnectionID-1] _ = x[coCompleteArrayExecution-2] _ = x[coClientLocale-3] _ = x[coSupportsLargeBulkOperations-4] _ = x[coDistributionEnabled-5] _ = x[coPrimaryConnectionID-6] _ = x[coPrimaryConnectionHost-7] _ = x[coPrimaryConnectionPort-8] _ = x[coCompleteDatatypeSupport-9] _ = x[coLargeNumberOfParametersSupport-10] _ = x[coSystemID-11] _ = x[coDataFormatVersion-12] _ = x[coAbapVarcharMode-13] _ = x[coSelectForUpdateSupported-14] _ = x[coClientDistributionMode-15] _ = x[coEngineDataFormatVersion-16] _ = x[coDistributionProtocolVersion-17] _ = x[coSplitBatchCommands-18] _ = x[coUseTransactionFlagsOnly-19] _ = x[coRowSlotImageParameter-20] _ = x[coIgnoreUnknownParts-21] _ = x[coTableOutputParameterMetadataSupport-22] _ = x[coDataFormatVersion2-23] _ = x[coItabParameter-24] _ = x[coDescribeTableOutputParameter-25] _ = x[coColumnarResultSet-26] _ = x[coScrollableResultSet-27] _ = x[coClientInfoNullValueSupported-28] _ = x[coAssociatedConnectionID-29] _ = x[coNonTransactionalPrepare-30] _ = x[coFdaEnabled-31] _ = x[coOSUser-32] _ = x[coRowSlotImageResultSet-33] _ = x[coEndianness-34] _ = x[coUpdateTopologyAnwhere-35] _ = x[coEnableArrayType-36] _ = x[coImplicitLobStreaming-37] _ = x[coCachedViewProperty-38] _ = x[coXOpenXAProtocolSupported-39] _ = x[coMasterCommitRedirectionSupported-40] _ = x[coActiveActiveProtocolVersion-41] _ = x[coActiveActiveConnectionOriginSite-42] _ = x[coQueryTimeoutSupported-43] _ = x[coFullVersionString-44] _ = x[coDatabaseName-45] _ = x[coBuildPlatform-46] _ = x[coImplicitXASessionSupported-47] _ = x[coClientSideColumnEncryptionVersion-48] _ = x[coCompressionLevelAndFlags-49] _ = x[coClientSideReExecutionSupported-50] _ = x[coClientReconnectWaitTimeout-51] _ = x[coOriginalAnchorConnectionID-52] _ = x[coFlagSet1-53] _ = x[coTopologyNetworkGroup-54] _ = x[coIPAddress-55] _ = x[coLRRPingTime-56] } const _connectOption_name = "coConnectionIDcoCompleteArrayExecutioncoClientLocalecoSupportsLargeBulkOperationscoDistributionEnabledcoPrimaryConnectionIDcoPrimaryConnectionHostcoPrimaryConnectionPortcoCompleteDatatypeSupportcoLargeNumberOfParametersSupportcoSystemIDcoDataFormatVersioncoAbapVarcharModecoSelectForUpdateSupportedcoClientDistributionModecoEngineDataFormatVersioncoDistributionProtocolVersioncoSplitBatchCommandscoUseTransactionFlagsOnlycoRowSlotImageParametercoIgnoreUnknownPartscoTableOutputParameterMetadataSupportcoDataFormatVersion2coItabParametercoDescribeTableOutputParametercoColumnarResultSetcoScrollableResultSetcoClientInfoNullValueSupportedcoAssociatedConnectionIDcoNonTransactionalPreparecoFdaEnabledcoOSUsercoRowSlotImageResultSetcoEndiannesscoUpdateTopologyAnwherecoEnableArrayTypecoImplicitLobStreamingcoCachedViewPropertycoXOpenXAProtocolSupportedcoMasterCommitRedirectionSupportedcoActiveActiveProtocolVersioncoActiveActiveConnectionOriginSitecoQueryTimeoutSupportedcoFullVersionStringcoDatabaseNamecoBuildPlatformcoImplicitXASessionSupportedcoClientSideColumnEncryptionVersioncoCompressionLevelAndFlagscoClientSideReExecutionSupportedcoClientReconnectWaitTimeoutcoOriginalAnchorConnectionIDcoFlagSet1coTopologyNetworkGroupcoIPAddresscoLRRPingTime" var _connectOption_index = [...]uint16{0, 14, 38, 52, 81, 102, 123, 146, 169, 194, 226, 236, 255, 272, 298, 322, 347, 376, 396, 421, 444, 464, 501, 521, 536, 566, 585, 606, 636, 660, 685, 697, 705, 728, 740, 763, 780, 802, 822, 848, 882, 911, 945, 968, 987, 1001, 1016, 1044, 1079, 1105, 1137, 1165, 1193, 1203, 1225, 1236, 1249} func (i connectOption) String() string { i -= 1 if i < 0 || i >= connectOption(len(_connectOption_index)-1) { return "connectOption(" + strconv.FormatInt(int64(i+1), 10) + ")" } return _connectOption_name[_connectOption_index[i]:_connectOption_index[i+1]] } golang-github-sap-go-hdb-0.100.10/internal/protocol/connectoptions.go000066400000000000000000000034301370256154600254610ustar00rootroot00000000000000/* 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/protocol/encoding" ) // client distribution mode //nolint const ( cdmOff optIntType = 0 cdmConnection optIntType = 1 cdmStatement optIntType = 2 cdmConnectionStatement optIntType = 3 ) // distribution protocol version //nolint const ( dpvBaseline = 0 dpvClientHandlesStatementSequence = 1 ) type connectOptions plainOptions func (o connectOptions) String() string { m := make(map[connectOption]interface{}) for k, v := range o { m[connectOption(k)] = v } return fmt.Sprintf("options %s", m) } func (o connectOptions) size() int { return plainOptions(o).size() } func (o connectOptions) numArg() int { return len(o) } func (o connectOptions) set(k connectOption, v interface{}) { o[k] = v } //linter:unused func (o connectOptions) get(k connectOption) (interface{}, bool) { v, ok := o[k] return v, ok } func (o *connectOptions) decode(dec *encoding.Decoder, ph *partHeader) error { *o = connectOptions{} // no reuse of maps - create new one plainOptions(*o).decode(dec, ph.numArg()) return dec.Error() } func (o connectOptions) encode(enc *encoding.Encoder) error { plainOptions(o).encode(enc) return nil } golang-github-sap-go-hdb-0.100.10/internal/protocol/convert.go000066400000000000000000000047161370256154600241040ustar00rootroot00000000000000/* Copyright 2020 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" ) // string / binary length indicators const ( bytesLenIndNullValue byte = 255 bytesLenIndSmall byte = 245 bytesLenIndMedium byte = 246 bytesLenIndBig byte = 247 ) const ( realNullValue uint32 = ^uint32(0) doubleNullValue uint64 = ^uint64(0) ) const ( longdateNullValue int64 = 3155380704000000001 seconddateNullValue int64 = 315538070401 daydateNullValue int32 = 3652062 secondtimeNullValue int32 = 86402 ) // Longdate 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)) } // nanosecond: HDB - 7 digits precision (not 9 digits) func convertTimeToLongdate(t time.Time) int64 { return (((((((convertTimeToDayDate(t)-1)*24)+int64(t.Hour()))*60)+int64(t.Minute()))*60)+int64(t.Second()))*10000000 + int64(t.Nanosecond()/100) + 1 } // Seconddate 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)) } func convertTimeToSeconddate(t time.Time) int64 { return (((((convertTimeToDayDate(t)-1)*24)+int64(t.Hour()))*60)+int64(t.Minute()))*60 + int64(t.Second()) + 1 } const julianHdb = 1721423 // 1 January 0001 00:00:00 (1721424) - 1 // Daydate func convertDaydateToTime(daydate int64) time.Time { return julianDayToTime(int(daydate) + julianHdb) } func convertTimeToDayDate(t time.Time) int64 { return int64(timeToJulianDay(t) - julianHdb) } // Secondtime 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 convertTimeToSecondtime(t time.Time) int { return (t.Hour()*60+t.Minute())*60 + t.Second() + 1 } golang-github-sap-go-hdb-0.100.10/internal/protocol/convert_test.go000066400000000000000000000126401370256154600251360ustar00rootroot00000000000000/* 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 ( "bytes" "errors" "math" "testing" "time" ) func assertEqualInt(t *testing.T, tc typeCode, v interface{}, r int64) { cv, err := tc.fieldType().Convert(v) if err != nil { t.Fatal(err) } if cv.(int64) != r { t.Fatalf("assert equal int failed %v - %d expected", cv, r) } } func assertEqualIntOutOfRangeError(t *testing.T, tc typeCode, v interface{}) { _, err := tc.fieldType().Convert(v) if !errors.Is(err, ErrIntegerOutOfRange) { t.Fatalf("assert equal out of range error failed %s %v", tc, v) } } func testConvertInteger(t *testing.T) { type testCustomInt int // integer data types assertEqualInt(t, tcTinyint, 42, 42) assertEqualInt(t, tcSmallint, 42, 42) assertEqualInt(t, tcInteger, 42, 42) assertEqualInt(t, tcBigint, 42, 42) // custom integer data type assertEqualInt(t, tcInteger, testCustomInt(42), 42) // integer reference i := 42 assertEqualInt(t, tcBigint, &i, 42) // min max values assertEqualIntOutOfRangeError(t, tcTinyint, minTinyint-1) assertEqualIntOutOfRangeError(t, tcTinyint, maxTinyint+1) assertEqualIntOutOfRangeError(t, tcSmallint, minSmallint-1) assertEqualIntOutOfRangeError(t, tcSmallint, maxSmallint+1) assertEqualIntOutOfRangeError(t, tcInteger, minInteger-1) assertEqualIntOutOfRangeError(t, tcInteger, maxInteger+1) // integer as string assertEqualInt(t, tcInteger, "42", 42) } func assertEqualFloat(t *testing.T, tc typeCode, v interface{}, r float64) { cv, err := tc.fieldType().Convert(v) if err != nil { t.Fatal(err) } if cv.(float64) != r { t.Fatalf("assert equal float failed %v - %f expected", cv, r) } } func assertEqualFloatOutOfRangeError(t *testing.T, tc typeCode, v interface{}) { _, err := tc.fieldType().Convert(v) if !errors.Is(err, ErrFloatOutOfRange) { t.Fatalf("assert equal out of range error failed %s %v", tc, v) } } func testConvertFloat(t *testing.T) { type testCustomFloat float32 realValue := float32(42.42) doubleValue := float64(42.42) stringDoubleValue := "42.42" // float data types assertEqualFloat(t, tcReal, realValue, float64(realValue)) assertEqualFloat(t, tcDouble, doubleValue, doubleValue) // custom float data type assertEqualFloat(t, tcReal, testCustomFloat(realValue), float64(realValue)) // float reference assertEqualFloat(t, tcReal, &realValue, float64(realValue)) // min max values assertEqualFloatOutOfRangeError(t, tcReal, math.Nextafter(maxReal, maxDouble)) assertEqualFloatOutOfRangeError(t, tcReal, math.Nextafter(maxReal, maxDouble)*-1) // float as string assertEqualFloat(t, tcDouble, stringDoubleValue, doubleValue) } func assertEqualTime(t *testing.T, v interface{}, r time.Time) { cv, err := tcTimestamp.fieldType().Convert(v) if err != nil { t.Fatal(err) } if !cv.(time.Time).Equal(r) { t.Fatalf("assert equal time failed %v - %v expected", cv, r) } } func testConvertTime(t *testing.T) { type testCustomTime time.Time timeValue := time.Now() // time data type assertEqualTime(t, timeValue, timeValue) // custom time data type assertEqualTime(t, testCustomTime(timeValue), timeValue) // time reference assertEqualTime(t, &timeValue, timeValue) } func assertEqualString(t *testing.T, tc typeCode, v interface{}, r string) { cv, err := tc.fieldType().Convert(v) if err != nil { t.Fatal(err) } if cv.(string) != r { t.Fatalf("assert equal string failed %v - %s expected", cv, r) } } func testConvertString(t *testing.T) { type testCustomString string stringValue := "Hello World" // string data types assertEqualString(t, tcString, stringValue, stringValue) // custom string data type assertEqualString(t, tcString, testCustomString(stringValue), stringValue) // string reference assertEqualString(t, tcString, &stringValue, stringValue) } func assertEqualBytes(t *testing.T, tc typeCode, v interface{}, r []byte) { cv, err := tc.fieldType().Convert(v) if err != nil { t.Fatal(err) } if bytes.Compare(cv.([]byte), r) != 0 { t.Fatalf("assert equal bytes failed %v - %v expected", cv, r) } } func testConvertBytes(t *testing.T) { type testCustomBytes []byte bytesValue := []byte("Hello World") // bytes data types assertEqualBytes(t, tcString, bytesValue, bytesValue) assertEqualBytes(t, tcBinary, bytesValue, bytesValue) // custom bytes data type assertEqualBytes(t, tcString, testCustomBytes(bytesValue), bytesValue) assertEqualBytes(t, tcBinary, testCustomBytes(bytesValue), bytesValue) // bytes reference assertEqualBytes(t, tcString, &bytesValue, bytesValue) assertEqualBytes(t, tcBinary, &bytesValue, bytesValue) } func TestConverter(t *testing.T) { tests := []struct { name string fct func(t *testing.T) }{ {"convertInteger", testConvertInteger}, {"convertFloat", testConvertFloat}, {"convertTime", testConvertTime}, {"convertString", testConvertString}, {"convertBytes", testConvertBytes}, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { test.fct(t) }) } } golang-github-sap-go-hdb-0.100.10/internal/protocol/datatype.go000066400000000000000000000040611370256154600242300ustar00rootroot00000000000000/* 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" "fmt" "reflect" "time" ) //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 DtRows ) // RegisterScanType registers driver owned datatype scantypes (e.g. Decimal, Lob). func RegisterScanType(dt DataType, scanType reflect.Type) { scanTypeMap[dt] = scanType } var scanTypeMap = map[DataType]reflect.Type{ DtUnknown: reflect.TypeOf((*interface{})(nil)).Elem(), DtTinyint: reflect.TypeOf((*uint8)(nil)).Elem(), DtSmallint: reflect.TypeOf((*int16)(nil)).Elem(), DtInteger: reflect.TypeOf((*int32)(nil)).Elem(), DtBigint: reflect.TypeOf((*int64)(nil)).Elem(), DtReal: reflect.TypeOf((*float32)(nil)).Elem(), DtDouble: reflect.TypeOf((*float64)(nil)).Elem(), DtTime: reflect.TypeOf((*time.Time)(nil)).Elem(), DtString: reflect.TypeOf((*string)(nil)).Elem(), DtBytes: reflect.TypeOf((*[]byte)(nil)).Elem(), DtDecimal: nil, // to be registered by driver DtLob: nil, // to be registered by driver DtRows: reflect.TypeOf((*sql.Rows)(nil)).Elem(), } // ScanType return the scan type (reflect.Type) of the corresponding data type. func (dt DataType) ScanType() reflect.Type { st, ok := scanTypeMap[dt] if !ok { panic(fmt.Sprintf("Missing ScanType for DataType %s", dt)) } return st } golang-github-sap-go-hdb-0.100.10/internal/protocol/datatype_string.go000066400000000000000000000016701370256154600256210ustar00rootroot00000000000000// Code generated by "stringer -type=DataType"; DO NOT EDIT. package protocol import "strconv" func _() { // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. var x [1]struct{} _ = x[DtUnknown-0] _ = x[DtTinyint-1] _ = x[DtSmallint-2] _ = x[DtInteger-3] _ = x[DtBigint-4] _ = x[DtReal-5] _ = x[DtDouble-6] _ = x[DtDecimal-7] _ = x[DtTime-8] _ = x[DtString-9] _ = x[DtBytes-10] _ = x[DtLob-11] _ = x[DtRows-12] } const _DataType_name = "DtUnknownDtTinyintDtSmallintDtIntegerDtBigintDtRealDtDoubleDtDecimalDtTimeDtStringDtBytesDtLobDtRows" var _DataType_index = [...]uint8{0, 9, 18, 28, 37, 45, 51, 59, 68, 74, 82, 89, 94, 100} 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.100.10/internal/protocol/doc.go000066400000000000000000000013221370256154600231570ustar00rootroot00000000000000/* 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.100.10/internal/protocol/encoding/000077500000000000000000000000001370256154600236535ustar00rootroot00000000000000golang-github-sap-go-hdb-0.100.10/internal/protocol/encoding/decode.go000066400000000000000000000141041370256154600254250ustar00rootroot00000000000000/* Copyright 2020 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 encoding import ( "encoding/binary" "io" "math" "github.com/SAP/go-hdb/internal/unicode" "golang.org/x/text/transform" ) const readScratchSize = 512 // used for skip as well - size not too small! // Decoder decodes hdb protocol datatypes an basis of an io.Reader. type Decoder struct { rd io.Reader err error b [readScratchSize]byte // scratch buffer tr transform.Transformer cnt int dfv int } // NewDecoder creates a new Decoder instance based on an io.Reader. func NewDecoder(rd io.Reader) *Decoder { return &Decoder{ rd: rd, tr: unicode.Cesu8ToUtf8Transformer, } } // Dfv returns the data format version. func (d *Decoder) Dfv() int { return d.dfv } // SetDfv sets the data format version. func (d *Decoder) SetDfv(dfv int) { d.dfv = dfv } // ResetCnt resets the byte read counter. func (d *Decoder) ResetCnt() { d.cnt = 0 } // Cnt returns the value of the byte read counter. func (d *Decoder) Cnt() int { return d.cnt } // Error returns the reader error. func (d *Decoder) Error() error { return d.err } // ResetError return and resets reader error. func (d *Decoder) ResetError() error { err := d.err d.err = nil return err } // Skip skips cnt bytes from reading. func (d *Decoder) Skip(cnt int) { var n int for n < cnt { if d.err != nil { return } to := cnt - n if to > readScratchSize { to = readScratchSize } var m int m, d.err = io.ReadFull(d.rd, d.b[:to]) n += m d.cnt += m } } // Byte reads and returns a byte. func (d *Decoder) Byte() byte { // ReadB as sig differs from ReadByte (vet issues) if d.err != nil { return 0 } var n int n, d.err = io.ReadFull(d.rd, d.b[:1]) d.cnt += n if d.err != nil { return 0 } return byte(d.b[0]) } // Bytes reads and returns a byte slice. func (d *Decoder) Bytes(p []byte) { if d.err != nil { return } var n int n, d.err = io.ReadFull(d.rd, p) d.cnt += n } // Bool reads and returns a boolean. func (d *Decoder) Bool() bool { if d.err != nil { return false } return d.Byte() != 0 } // Int8 reads and returns an int8. func (d *Decoder) Int8() int8 { return int8(d.Byte()) } // Int16 reads and returns an int16. func (d *Decoder) Int16() int16 { if d.err != nil { return 0 } var n int n, d.err = io.ReadFull(d.rd, d.b[:2]) d.cnt += n if d.err != nil { return 0 } return int16(binary.LittleEndian.Uint16(d.b[:2])) } // Uint16 reads and returns an uint16. func (d *Decoder) Uint16() uint16 { if d.err != nil { return 0 } var n int n, d.err = io.ReadFull(d.rd, d.b[:2]) d.cnt += n if d.err != nil { return 0 } return binary.LittleEndian.Uint16(d.b[:2]) } // Int32 reads and returns an int32. func (d *Decoder) Int32() int32 { if d.err != nil { return 0 } var n int n, d.err = io.ReadFull(d.rd, d.b[:4]) d.cnt += n if d.err != nil { return 0 } return int32(binary.LittleEndian.Uint32(d.b[:4])) } // Uint32 reads and returns an uint32. func (d *Decoder) Uint32() uint32 { if d.err != nil { return 0 } var n int n, d.err = io.ReadFull(d.rd, d.b[:4]) d.cnt += n if d.err != nil { return 0 } return binary.LittleEndian.Uint32(d.b[:4]) } // Uint32ByteOrder reads and returns an uint32 in given byte order. func (d *Decoder) Uint32ByteOrder(byteOrder binary.ByteOrder) uint32 { if d.err != nil { return 0 } var n int n, d.err = io.ReadFull(d.rd, d.b[:4]) d.cnt += n if d.err != nil { return 0 } return byteOrder.Uint32(d.b[:4]) } // Int64 reads and returns an int64. func (d *Decoder) Int64() int64 { if d.err != nil { return 0 } var n int n, d.err = io.ReadFull(d.rd, d.b[:8]) d.cnt += n if d.err != nil { return 0 } return int64(binary.LittleEndian.Uint64(d.b[:8])) } // Uint64 reads and returns an uint64. func (d *Decoder) Uint64() uint64 { if d.err != nil { return 0 } var n int n, d.err = io.ReadFull(d.rd, d.b[:8]) d.cnt += n if d.err != nil { return 0 } return binary.LittleEndian.Uint64(d.b[:8]) } // Float32 reads and returns a float32. func (d *Decoder) Float32() float32 { if d.err != nil { return 0 } var n int n, d.err = io.ReadFull(d.rd, d.b[:4]) d.cnt += n if d.err != nil { return 0 } bits := binary.LittleEndian.Uint32(d.b[:4]) return math.Float32frombits(bits) } // Float64 reads and returns a float64. func (d *Decoder) Float64() float64 { if d.err != nil { return 0 } var n int n, d.err = io.ReadFull(d.rd, d.b[:8]) d.cnt += n if d.err != nil { return 0 } bits := binary.LittleEndian.Uint64(d.b[:8]) return math.Float64frombits(bits) } // CESU8Bytes reads a size CESU-8 encoded byte sequence and returns an UTF-8 byte slice. func (d *Decoder) CESU8Bytes(size int) []byte { if d.err != nil { return nil } p := make([]byte, size) var n int n, d.err = io.ReadFull(d.rd, p) d.cnt += n if d.err != nil { return nil } d.tr.Reset() if n, _, d.err = d.tr.Transform(p, p, true); d.err != nil { // inplace transformation return nil } return p[:n] } // // ShortCESU8Bytes reads a CESU-8 encoded byte sequence and returns an UTF-8 byte slice. // // Size is encoded in one byte. // func (d *Decoder) ShortCESU8Bytes() ([]byte, int) { // size := d.Byte() // return d.CESU8Bytes(int(size)), int(size) // } // // ShortCESU8String reads a CESU-8 encoded byte sequence and returns an UTF-8 string. // // Size is encoded in one byte. // func (d *Decoder) ShortCESU8Bytes() (string, int) { // b, n := d.ShortCESU8Bytes() // return b, n // } // // ShortBytes reads a byte sequence and returns a byte slice. // // Size is encoded in one byte. // func (d *Decoder) ShortBytes() ([]byte, int) { // size := d.Byte() // b := make([]byte, size) // d.Bytes(b) // return b // } golang-github-sap-go-hdb-0.100.10/internal/protocol/encoding/doc.go000066400000000000000000000011361370256154600247500ustar00rootroot00000000000000/* 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 encoding implements ... package encoding golang-github-sap-go-hdb-0.100.10/internal/protocol/encoding/encode.go000066400000000000000000000100711370256154600254360ustar00rootroot00000000000000/* Copyright 2020 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 encoding import ( "encoding/binary" "io" "math" "github.com/SAP/go-hdb/internal/unicode" "golang.org/x/text/transform" ) const writeScratchSize = 4096 // Encoder encodes hdb protocol datatypes an basis of an io.Writer. type Encoder struct { wr io.Writer err error b []byte // scratch buffer (min 8 Bytes) tr transform.Transformer } // NewEncoder creates a new Encoder instance. func NewEncoder(wr io.Writer) *Encoder { return &Encoder{ wr: wr, b: make([]byte, writeScratchSize), tr: unicode.Utf8ToCesu8Transformer, } } // Zeroes writes cnt zero byte values. func (e *Encoder) Zeroes(cnt int) { if e.err != nil { return } // zero out scratch area l := cnt if l > len(e.b) { l = len(e.b) } for i := 0; i < l; i++ { e.b[i] = 0 } for i := 0; i < cnt; { j := cnt - i if j > len(e.b) { j = len(e.b) } n, _ := e.wr.Write(e.b[:j]) if n != j { return } i += n } } // Bytes writes a bytes slice. func (e *Encoder) Bytes(p []byte) { if e.err != nil { return } e.wr.Write(p) } // Byte writes a byte. func (e *Encoder) Byte(b byte) { // WriteB as sig differs from WriteByte (vet issues) if e.err != nil { return } e.b[0] = b e.Bytes(e.b[:1]) } // Bool writes a boolean. func (e *Encoder) Bool(v bool) { if e.err != nil { return } if v { e.Byte(1) } else { e.Byte(0) } } // Int8 writes an int8. func (e *Encoder) Int8(i int8) { if e.err != nil { return } e.Byte(byte(i)) } // Int16 writes an int16. func (e *Encoder) Int16(i int16) { if e.err != nil { return } binary.LittleEndian.PutUint16(e.b[:2], uint16(i)) e.wr.Write(e.b[:2]) } // Uint16 writes an uint16. func (e *Encoder) Uint16(i uint16) { if e.err != nil { return } binary.LittleEndian.PutUint16(e.b[:2], i) e.wr.Write(e.b[:2]) } // Int32 writes an int32. func (e *Encoder) Int32(i int32) { if e.err != nil { return } binary.LittleEndian.PutUint32(e.b[:4], uint32(i)) e.wr.Write(e.b[:4]) } // Uint32 writes an uint32. func (e *Encoder) Uint32(i uint32) { if e.err != nil { return } binary.LittleEndian.PutUint32(e.b[:4], i) e.wr.Write(e.b[:4]) } // Int64 writes an int64. func (e *Encoder) Int64(i int64) { if e.err != nil { return } binary.LittleEndian.PutUint64(e.b[:8], uint64(i)) e.wr.Write(e.b[:8]) } // Uint64 writes an uint64. func (e *Encoder) Uint64(i uint64) { if e.err != nil { return } binary.LittleEndian.PutUint64(e.b[:8], i) e.wr.Write(e.b[:8]) } // Float32 writes a float32. func (e *Encoder) Float32(f float32) { if e.err != nil { return } bits := math.Float32bits(f) binary.LittleEndian.PutUint32(e.b[:4], bits) e.wr.Write(e.b[:4]) } // Float64 writes a float64. func (e *Encoder) Float64(f float64) { if e.err != nil { return } bits := math.Float64bits(f) binary.LittleEndian.PutUint64(e.b[:8], bits) e.wr.Write(e.b[:8]) } // String writes a string. func (e *Encoder) String(s string) { if e.err != nil { return } e.Bytes([]byte(s)) } // CESU8Bytes writes an UTF-8 byte slice as CESU-8 and returns the CESU-8 bytes written. func (e *Encoder) CESU8Bytes(p []byte) int { if e.err != nil { return 0 } e.tr.Reset() cnt := 0 i := 0 for i < len(p) { m, n, err := e.tr.Transform(e.b, p[i:], true) if err != nil && err != transform.ErrShortDst { e.err = err return cnt } if m == 0 { e.err = transform.ErrShortDst return cnt } o, _ := e.wr.Write(e.b[:m]) cnt += o i += n } return cnt } // CESU8String is like WriteCesu8 with an UTF-8 string as parameter. func (e *Encoder) CESU8String(s string) int { return e.CESU8Bytes([]byte(s)) } golang-github-sap-go-hdb-0.100.10/internal/protocol/endianess.go000066400000000000000000000013151370256154600243650ustar00rootroot00000000000000/* 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 //nolint:deadcode littleEndian endianess = 1 ) golang-github-sap-go-hdb-0.100.10/internal/protocol/endianess_string.go000066400000000000000000000012041370256154600257500ustar00rootroot00000000000000// Code generated by "stringer -type=endianess"; DO NOT EDIT. package protocol import "strconv" func _() { // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. var x [1]struct{} _ = x[bigEndian-0] _ = x[littleEndian-1] } 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.100.10/internal/protocol/error.go000066400000000000000000000126031370256154600235470ustar00rootroot00000000000000/* 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/protocol/encoding" ) 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 { if e.errors == nil { return 0 } return len(e.errors) } // SetIdx implements the driver.Error interface. func (e *hdbErrors) SetIdx(idx int) { numError := e.NumError() switch { case idx < 0: e.idx = 0 case idx >= numError: e.idx = numError - 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.NumError() { 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) reset(numArg int) { e.idx = 0 // init error index if e.errors == nil || numArg > cap(e.errors) { e.errors = make([]*hdbError, numArg) } else { e.errors = e.errors[:numArg] } } func (e *hdbErrors) decode(dec *encoding.Decoder, ph *partHeader) error { e.reset(ph.numArg()) numArg := ph.numArg() for i := 0; i < numArg; i++ { _error := e.errors[i] if _error == nil { _error = new(hdbError) e.errors[i] = _error } _error.stmtNo = -1 _error.errorCode = dec.Int32() _error.errorPosition = dec.Int32() _error.errorTextLength = dec.Int32() _error.errorLevel = errorLevel(dec.Int8()) dec.Bytes(_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)) dec.Bytes(_error.errorText) if 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) dec.Skip(1) break } pad := padBytes(int(fixLength + _error.errorTextLength)) if pad != 0 { dec.Skip(pad) } } return dec.Error() } golang-github-sap-go-hdb-0.100.10/internal/protocol/errorlevel.go000066400000000000000000000016661370256154600246060ustar00rootroot00000000000000/* 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.100.10/internal/protocol/fetchsize.go000066400000000000000000000017141370256154600244030ustar00rootroot00000000000000/* 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/protocol/encoding" ) //fetch size type fetchsize int32 func (s fetchsize) String() string { return fmt.Sprintf("fetchsize %d", s) } func (s *fetchsize) decode(dec *encoding.Decoder, ph *partHeader) error { *s = fetchsize(dec.Int32()) return dec.Error() } func (s fetchsize) encode(enc *encoding.Encoder) error { enc.Int32(int32(s)); return nil } golang-github-sap-go-hdb-0.100.10/internal/protocol/field.go000066400000000000000000000044121370256154600235000ustar00rootroot00000000000000/* 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" "sort" "github.com/SAP/go-hdb/internal/protocol/encoding" ) const noFieldName uint32 = 0xFFFFFFFF type offsetName struct { offset uint32 name string } type fieldNames []offsetName func (n fieldNames) search(offset uint32) int { // binary search return sort.Search(len(n), func(i int) bool { return n[i].offset >= offset }) } func (n *fieldNames) insert(offset uint32) { if offset == noFieldName { return } i := n.search(offset) switch { case i >= len(*n): // not found -> append *n = append(*n, offsetName{offset: offset}) case (*n)[i].offset == offset: // duplicate default: // insert *n = append(*n, offsetName{}) copy((*n)[i+1:], (*n)[i:]) (*n)[i] = offsetName{offset: offset} } } func (n fieldNames) name(offset uint32) string { i := n.search(offset) if i < len(n) { return n[i].name } return "" } func (n fieldNames) decode(dec *encoding.Decoder) { // TODO sniffer - python client texts are returned differently? // - double check offset calc (CESU8 issue?) pos := uint32(0) for i, on := range n { diff := int(on.offset - pos) if diff > 0 { dec.Skip(diff) } size := int(dec.Byte()) b := dec.CESU8Bytes(size) n[i].name = string(b) pos += uint32(1 + size + diff) // len byte + size + diff } } // A Field represents whether a db result or a parameter Field. type Field interface { Name() string TypeName() string TypeLength() (int64, bool) TypePrecisionScale() (int64, int64, bool) ScanType() DataType Nullable() bool In() bool Out() bool Converter() Converter } var ( _ Field = (*resultField)(nil) _ Field = (*parameterField)(nil) ) // TODO cache func newFieldValues(size int) []driver.Value { return make([]driver.Value, size) } golang-github-sap-go-hdb-0.100.10/internal/protocol/fieldtype.go000066400000000000000000000677021370256154600244150ustar00rootroot00000000000000/* Copyright 2020 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" "errors" "fmt" "io" "math" "reflect" "strconv" "time" "github.com/SAP/go-hdb/internal/protocol/encoding" "github.com/SAP/go-hdb/internal/unicode/cesu8" ) 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 ) type locatorID uint64 // byte[locatorIdSize] // ErrUint64OutOfRange means that a uint64 exceeds the size of a int64. var ErrUint64OutOfRange = errors.New("uint64 values with high bit set are not supported") // 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 timeReflectType = reflect.TypeOf((*time.Time)(nil)).Elem() var bytesReflectType = reflect.TypeOf((*[]byte)(nil)).Elem() var stringReflectType = reflect.TypeOf((*string)(nil)).Elem() var zeroTime = time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC) const ( tinyintFieldSize = 1 smallintFieldSize = 2 integerFieldSize = 4 bigintFieldSize = 8 realFieldSize = 4 doubleFieldSize = 8 dateFieldSize = 4 timeFieldSize = 4 timestampFieldSize = dateFieldSize + timeFieldSize longdateFieldSize = 8 seconddateFieldSize = 8 daydateFieldSize = 4 secondtimeFieldSize = 4 decimalFieldSize = 16 lobInputParametersSize = 9 ) // Converter is the interface that wraps the Convert method. // Convert is used to convert query parameters from go datatypes to hdb datatypes. type Converter interface { Convert(interface{}) (interface{}, error) } type fieldType interface { Converter prmSize(interface{}) int encodePrm(*encoding.Encoder, interface{}) error } // can use decoder for parameter and result fields type commonDecoder interface { decode(*encoding.Decoder) (interface{}, error) } // specific parameter decoder type prmDecoder interface { decodePrm(*encoding.Decoder) (interface{}, error) } // specific result decoder type resDecoder interface { decodeRes(*encoding.Decoder) (interface{}, error) } /* (*1) HDB bug: secondtime null value cannot be set by setting high bit - trying so, gives: SQL HdbError 1033 - error while parsing protocol: no such data type: type_code=192, index=2 Traffic analysis of python client (https://pypi.org/project/hdbcli) resulted in: - set null value constant directly instead of using high bit Please see handling of this special case in: - fieldSize() - writeParameterField() */ // parameter size func prmSize(tc typeCode, arg driver.NamedValue) int { v := arg.Value if v == nil && tc != tcSecondtime { // secondTime exception (see (*1)) return 0 } return tc.fieldType().prmSize(v) } // encode parameter func encodePrm(e *encoding.Encoder, tc typeCode, arg driver.NamedValue) error { v := arg.Value encTc := tc.encTc() if v == nil && tc != tcSecondtime { // secondTime exception (see (*1)) e.Byte(byte(encTc) | 0x80) // type code null value: set high bit return nil } e.Byte(byte(encTc)) // type code return tc.fieldType().encodePrm(e, v) } /* decode parameter - used for Sniffer - type code is first byte (see encodePrm) */ func decodePrm(d *encoding.Decoder) (typeCode, interface{}, error) { tc := typeCode(d.Byte()) if tc&0x80 != 0 { // high bit set -> null value return tc, nil, nil } ft := tc.fieldType() switch ft := ft.(type) { default: panic("field type missing decoder") case prmDecoder: v, err := ft.decodePrm(d) return tc, v, err case commonDecoder: v, err := ft.decode(d) return tc, v, err } } /* decode result */ func decodeRes(d *encoding.Decoder, tc typeCode) (interface{}, error) { ft := tc.fieldType() switch ft := ft.(type) { default: panic("field type missing decoder") case resDecoder: return ft.decodeRes(d) case commonDecoder: return ft.decode(d) } } var ( tinyintType = _tinyintType{} smallintType = _smallintType{} integerType = _integerType{} bigintType = _bigintType{} realType = _realType{} doubleType = _doubleType{} dateType = _dateType{} timeType = _timeType{} timestampType = _timestampType{} longdateType = _longdateType{} seconddateType = _seconddateType{} daydateType = _daydateType{} secondtimeType = _secondtimeType{} decimalType = _decimalType{} varType = _varType{} alphaType = _alphaType{} cesu8Type = _cesu8Type{} lobVarType = _lobVarType{} lobCESU8Type = _lobCESU8Type{} ) type _tinyintType struct{} type _smallintType struct{} type _integerType struct{} type _bigintType struct{} type _realType struct{} type _doubleType struct{} type _dateType struct{} type _timeType struct{} type _timestampType struct{} type _longdateType struct{} type _seconddateType struct{} type _daydateType struct{} type _secondtimeType struct{} type _decimalType struct{} type _varType struct{} type _alphaType struct{} type _cesu8Type struct{} type _lobVarType struct{} type _lobCESU8Type struct{} var ( _ fieldType = (*_tinyintType)(nil) _ fieldType = (*_smallintType)(nil) _ fieldType = (*_integerType)(nil) _ fieldType = (*_bigintType)(nil) _ fieldType = (*_realType)(nil) _ fieldType = (*_doubleType)(nil) _ fieldType = (*_dateType)(nil) _ fieldType = (*_timeType)(nil) _ fieldType = (*_timestampType)(nil) _ fieldType = (*_longdateType)(nil) _ fieldType = (*_seconddateType)(nil) _ fieldType = (*_daydateType)(nil) _ fieldType = (*_secondtimeType)(nil) _ fieldType = (*_decimalType)(nil) _ fieldType = (*_varType)(nil) _ fieldType = (*_alphaType)(nil) _ fieldType = (*_cesu8Type)(nil) _ fieldType = (*_lobVarType)(nil) _ fieldType = (*_lobCESU8Type)(nil) ) // A ConvertError is returned by conversion methods if a go datatype to hdb datatype conversion fails. type ConvertError struct { err error ft fieldType v interface{} } func (e *ConvertError) Error() string { return fmt.Sprintf("unsupported %[1]s conversion: %[2]T %[2]v", e.ft, e.v) } // Unwrap returns the nested error. func (e *ConvertError) Unwrap() error { return e.err } func newConvertError(ft fieldType, v interface{}, err error) *ConvertError { return &ConvertError{ft: ft, v: v, err: err} } func (_tinyintType) String() string { return "tinyintType" } func (_smallintType) String() string { return "smallintType" } func (_integerType) String() string { return "integerType" } func (_bigintType) String() string { return "bigintType" } func (_realType) String() string { return "realType" } func (_doubleType) String() string { return "doubleType" } func (_dateType) String() string { return "dateType" } func (_timeType) String() string { return "timeType" } func (_timestampType) String() string { return "timestampType" } func (_longdateType) String() string { return "longdateType" } func (_seconddateType) String() string { return "seconddateType" } func (_daydateType) String() string { return "daydateType" } func (_secondtimeType) String() string { return "secondtimeType" } func (_decimalType) String() string { return "decimalType" } func (_varType) String() string { return "varType" } func (_alphaType) String() string { return "alphaType" } func (_cesu8Type) String() string { return "cesu8Type" } func (_lobVarType) String() string { return "lobVarType" } func (_lobCESU8Type) String() string { return "lobCESU8Type" } func (ft _tinyintType) Convert(v interface{}) (interface{}, error) { return convertInteger(ft, v, minTinyint, maxTinyint) } func (ft _smallintType) Convert(v interface{}) (interface{}, error) { return convertInteger(ft, v, minSmallint, maxSmallint) } func (ft _integerType) Convert(v interface{}) (interface{}, error) { return convertInteger(ft, v, minInteger, maxInteger) } func (ft _bigintType) Convert(v interface{}) (interface{}, error) { return convertInteger(ft, v, minBigint, maxBigint) } // integer types func convertInteger(ft fieldType, v interface{}, min, max int64) (driver.Value, error) { if v == nil { return v, nil } i64, err := convertToInt64(ft, v) if err != nil { return nil, err } if i64 > max || i64 < min { return nil, newConvertError(ft, v, ErrIntegerOutOfRange) } return i64, nil } func convertToInt64(ft fieldType, v interface{}) (int64, error) { rv := reflect.ValueOf(v) switch rv.Kind() { // bool is represented in HDB as tinyint case reflect.Bool: if rv.Bool() { return 1, nil } return 0, nil case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: return rv.Int(), nil case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: u64 := rv.Uint() if u64 >= 1<<63 { return 0, newConvertError(ft, v, ErrUint64OutOfRange) } return int64(u64), nil case reflect.Float32, reflect.Float64: f64 := rv.Float() i64 := int64(f64) if f64 != float64(i64) { // should work for overflow, NaN, +-INF as well return 0, newConvertError(ft, v, nil) } return i64, nil case reflect.String: i64, err := strconv.ParseInt(rv.String(), 10, 64) if err != nil { return 0, newConvertError(ft, v, nil) } return i64, nil case reflect.Ptr: // indirect pointers if rv.IsNil() { return 0, nil } return convertToInt64(ft, rv.Elem().Interface()) } if rv.Type().ConvertibleTo(stringReflectType) { return convertToInt64(ft, rv.Convert(stringReflectType).Interface()) } return 0, newConvertError(ft, v, nil) } func (ft _realType) Convert(v interface{}) (interface{}, error) { return convertFloat(ft, v, maxReal) } func (ft _doubleType) Convert(v interface{}) (interface{}, error) { return convertFloat(ft, v, maxDouble) } // float types func convertFloat(ft fieldType, v interface{}, max float64) (driver.Value, error) { if v == nil { return v, nil } f64, err := convertToFloat64(ft, v) if err != nil { return nil, err } if math.Abs(f64) > max { return nil, newConvertError(ft, v, ErrFloatOutOfRange) } return f64, nil } func convertToFloat64(ft fieldType, v interface{}) (float64, error) { rv := reflect.ValueOf(v) switch rv.Kind() { case reflect.Float32, reflect.Float64: return rv.Float(), nil case reflect.String: f64, err := strconv.ParseFloat(rv.String(), 64) if err != nil { return 0, newConvertError(ft, v, nil) } return f64, nil case reflect.Ptr: // indirect pointers if rv.IsNil() { return 0, nil } return convertToFloat64(ft, rv.Elem().Interface()) } if rv.Type().ConvertibleTo(stringReflectType) { return convertToFloat64(ft, rv.Convert(stringReflectType).Interface()) } return 0, newConvertError(ft, v, nil) } func (ft _dateType) Convert(v interface{}) (interface{}, error) { return convertTime(ft, v) } func (ft _timeType) Convert(v interface{}) (interface{}, error) { return convertTime(ft, v) } func (ft _timestampType) Convert(v interface{}) (interface{}, error) { return convertTime(ft, v) } func (ft _longdateType) Convert(v interface{}) (interface{}, error) { return convertTime(ft, v) } func (ft _seconddateType) Convert(v interface{}) (interface{}, error) { return convertTime(ft, v) } func (ft _daydateType) Convert(v interface{}) (interface{}, error) { return convertTime(ft, v) } func (ft _secondtimeType) Convert(v interface{}) (interface{}, error) { return convertTime(ft, v) } // time func convertTime(ft fieldType, 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 convertTime(ft, rv.Elem().Interface()) } if rv.Type().ConvertibleTo(timeReflectType) { tv := rv.Convert(timeReflectType) return tv.Interface().(time.Time), nil } return nil, newConvertError(ft, v, nil) } func (ft _decimalType) Convert(v interface{}) (interface{}, error) { return convertDecimal(ft, v) } // decimal func convertDecimal(ft fieldType, v interface{}) (driver.Value, error) { if v == nil { return nil, nil } if v, ok := v.([]byte); ok { return v, nil } return nil, newConvertError(ft, v, nil) } func (ft _varType) Convert(v interface{}) (interface{}, error) { return convertBytes(ft, v) } func (ft _alphaType) Convert(v interface{}) (interface{}, error) { return convertBytes(ft, v) } func (ft _cesu8Type) Convert(v interface{}) (interface{}, error) { return convertBytes(ft, v) } // bytes func convertBytes(ft fieldType, 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() == bytesReflectType { return rv.Bytes(), nil } case reflect.Ptr: // indirect pointers if rv.IsNil() { return nil, nil } return convertBytes(ft, rv.Elem().Interface()) } if rv.Type().ConvertibleTo(bytesReflectType) { bv := rv.Convert(bytesReflectType) return bv.Interface().([]byte), nil } return nil, newConvertError(ft, v, nil) } func (ft _lobVarType) Convert(v interface{}) (interface{}, error) { return convertLob(false, ft, v) } func (ft _lobCESU8Type) Convert(v interface{}) (interface{}, error) { return convertLob(true, ft, v) } // ReadProvider is the interface wrapping the Reader which provides an io.Reader. type ReadProvider interface { Reader() io.Reader } // Lob func convertLob(isCharBased bool, ft fieldType, v interface{}) (driver.Value, error) { if v == nil { return v, nil } switch v := v.(type) { case io.Reader: return v, nil case ReadProvider: return v.Reader(), nil default: return nil, newConvertError(ft, v, nil) } } func (_tinyintType) prmSize(interface{}) int { return tinyintFieldSize } func (_smallintType) prmSize(interface{}) int { return smallintFieldSize } func (_integerType) prmSize(interface{}) int { return integerFieldSize } func (_bigintType) prmSize(interface{}) int { return bigintFieldSize } func (_realType) prmSize(interface{}) int { return realFieldSize } func (_doubleType) prmSize(interface{}) int { return doubleFieldSize } func (_dateType) prmSize(interface{}) int { return dateFieldSize } func (_timeType) prmSize(interface{}) int { return timeFieldSize } func (_timestampType) prmSize(interface{}) int { return timestampFieldSize } func (_longdateType) prmSize(interface{}) int { return longdateFieldSize } func (_seconddateType) prmSize(interface{}) int { return seconddateFieldSize } func (_daydateType) prmSize(interface{}) int { return daydateFieldSize } func (_secondtimeType) prmSize(interface{}) int { return secondtimeFieldSize } func (_decimalType) prmSize(interface{}) int { return decimalFieldSize } func (_lobVarType) prmSize(v interface{}) int { return lobInputParametersSize } func (_lobCESU8Type) prmSize(v interface{}) int { return lobInputParametersSize } func (ft _varType) prmSize(v interface{}) int { switch v := v.(type) { case []byte: return varBytesSize(ft, len(v)) case string: return varBytesSize(ft, len(v)) default: return -1 } } func (ft _alphaType) prmSize(v interface{}) int { return varType.prmSize(v) } func (ft _cesu8Type) prmSize(v interface{}) int { switch v := v.(type) { case []byte: return varBytesSize(ft, cesu8.Size(v)) case string: return varBytesSize(ft, cesu8.StringSize(v)) default: return -1 } } func varBytesSize(ft fieldType, size int) int { switch { default: return -1 case size <= int(bytesLenIndSmall): return size + 1 case size <= math.MaxInt16: return size + 3 case size <= math.MaxInt32: return size + 5 } } func (ft _tinyintType) encodePrm(e *encoding.Encoder, v interface{}) error { i, err := asInt64(ft, v) if err != nil { return err } e.Byte(byte(i)) return nil } func (ft _smallintType) encodePrm(e *encoding.Encoder, v interface{}) error { i, err := asInt64(ft, v) if err != nil { return err } e.Int16(int16(i)) return nil } func (ft _integerType) encodePrm(e *encoding.Encoder, v interface{}) error { i, err := asInt64(ft, v) if err != nil { return err } e.Int32(int32(i)) return nil } func (ft _bigintType) encodePrm(e *encoding.Encoder, v interface{}) error { i, err := asInt64(ft, v) if err != nil { return err } e.Int64(i) return nil } func asInt64(ft fieldType, v interface{}) (int64, error) { switch v := v.(type) { default: return 0, newConvertError(ft, v, nil) case bool: if v { return 1, nil } return 0, nil case int64: return v, nil } } func (ft _realType) encodePrm(e *encoding.Encoder, v interface{}) error { switch v := v.(type) { case float64: e.Float32(float32(v)) return nil default: return newConvertError(ft, v, nil) } } func (ft _doubleType) encodePrm(e *encoding.Encoder, v interface{}) error { switch v := v.(type) { case float64: e.Float64(v) return nil default: return newConvertError(ft, v, nil) } } func (ft _dateType) encodePrm(e *encoding.Encoder, v interface{}) error { t, err := asTime(ft, v) if err != nil { return err } encodeDate(e, t) return nil } func (ft _timeType) encodePrm(e *encoding.Encoder, v interface{}) error { t, err := asTime(ft, v) if err != nil { return err } encodeTime(e, t) return nil } func (ft _timestampType) encodePrm(e *encoding.Encoder, v interface{}) error { t, err := asTime(ft, v) if err != nil { return err } encodeDate(e, t) encodeTime(e, t) return nil } func encodeDate(e *encoding.Encoder, t time.Time) { // year: set most sig bit // month 0 based year, month, day := t.Date() e.Uint16(uint16(year) | 0x8000) e.Int8(int8(month) - 1) e.Int8(int8(day)) } func encodeTime(e *encoding.Encoder, t time.Time) { e.Byte(byte(t.Hour()) | 0x80) e.Int8(int8(t.Minute())) millisecs := t.Second()*1000 + t.Nanosecond()/1000000 e.Uint16(uint16(millisecs)) } func (ft _longdateType) encodePrm(e *encoding.Encoder, v interface{}) error { t, err := asTime(ft, v) if err != nil { return err } e.Int64(convertTimeToLongdate(t)) return nil } func (ft _seconddateType) encodePrm(e *encoding.Encoder, v interface{}) error { t, err := asTime(ft, v) if err != nil { return err } e.Int64(convertTimeToSeconddate(t)) return nil } func (ft _daydateType) encodePrm(e *encoding.Encoder, v interface{}) error { t, err := asTime(ft, v) if err != nil { return err } e.Int32(int32(convertTimeToDayDate(t))) return nil } func (ft _secondtimeType) encodePrm(e *encoding.Encoder, v interface{}) error { if v == nil { e.Int32(secondtimeNullValue) return nil } t, err := asTime(ft, v) if err != nil { return err } e.Int32(int32(convertTimeToSecondtime(t))) return nil } func asTime(ft fieldType, v interface{}) (time.Time, error) { t, ok := v.(time.Time) if !ok { return zeroTime, newConvertError(ft, v, nil) } //store in utc return t.UTC(), nil } func (ft _decimalType) encodePrm(e *encoding.Encoder, v interface{}) error { p, ok := v.([]byte) if !ok { return newConvertError(ft, v, nil) } if len(p) != decimalFieldSize { return fmt.Errorf("invalid argument length %d - expected %d", len(p), decimalFieldSize) } e.Bytes(p) return nil } func (ft _varType) encodePrm(e *encoding.Encoder, v interface{}) error { switch v := v.(type) { case []byte: return encodeVarBytes(e, v) case string: return encodeVarString(e, v) default: return newConvertError(ft, v, nil) } } func (ft _alphaType) encodePrm(e *encoding.Encoder, v interface{}) error { return varType.encodePrm(e, v) } func encodeVarBytesSize(e *encoding.Encoder, size int) error { switch { default: return fmt.Errorf("max argument length %d of string exceeded", size) case size <= int(bytesLenIndSmall): e.Byte(byte(size)) case size <= math.MaxInt16: e.Byte(bytesLenIndMedium) e.Int16(int16(size)) case size <= math.MaxInt32: e.Byte(bytesLenIndBig) e.Int32(int32(size)) } return nil } func encodeVarBytes(e *encoding.Encoder, p []byte) error { if err := encodeVarBytesSize(e, len(p)); err != nil { return err } e.Bytes(p) return nil } func encodeVarString(e *encoding.Encoder, s string) error { if err := encodeVarBytesSize(e, len(s)); err != nil { return err } e.String(s) return nil } func (ft _cesu8Type) encodePrm(e *encoding.Encoder, v interface{}) error { switch v := v.(type) { case []byte: return encodeCESU8Bytes(e, v) case string: return encodeCESU8String(e, v) default: return newConvertError(ft, v, nil) } } func encodeCESU8Bytes(e *encoding.Encoder, p []byte) error { size := cesu8.Size(p) if err := encodeVarBytesSize(e, size); err != nil { return err } e.CESU8Bytes(p) return nil } func encodeCESU8String(e *encoding.Encoder, s string) error { size := cesu8.StringSize(s) if err := encodeVarBytesSize(e, size); err != nil { return err } e.CESU8String(s) return nil } func (ft _lobVarType) encodePrm(e *encoding.Encoder, v interface{}) error { switch v := v.(type) { case *lobInDescr: return encodeLobPrm(e, v) case io.Reader: //TODO check if keep descr := &lobInDescr{} return encodeLobPrm(e, descr) default: return newConvertError(ft, v, nil) } } func (ft _lobCESU8Type) encodePrm(e *encoding.Encoder, v interface{}) error { switch v := v.(type) { case *lobInDescr: return encodeLobPrm(e, v) case io.Reader: //TODO check if keep descr := &lobInDescr{} return encodeLobPrm(e, descr) default: return newConvertError(ft, v, nil) } } func encodeLobPrm(e *encoding.Encoder, descr *lobInDescr) error { e.Byte(byte(descr.opt)) e.Int32(descr.size) e.Int32(descr.pos) return nil } func (_tinyintType) decodePrm(d *encoding.Decoder) (interface{}, error) { return int64(d.Byte()), nil } func (_smallintType) decodePrm(d *encoding.Decoder) (interface{}, error) { return int64(d.Int16()), nil } func (_integerType) decodePrm(d *encoding.Decoder) (interface{}, error) { return int64(d.Int32()), nil } func (_bigintType) decodePrm(d *encoding.Decoder) (interface{}, error) { return d.Int64(), nil } func (ft _tinyintType) decodeRes(d *encoding.Decoder) (interface{}, error) { if !d.Bool() { //null value return nil, nil } return ft.decodePrm(d) } func (ft _smallintType) decodeRes(d *encoding.Decoder) (interface{}, error) { if !d.Bool() { //null value return nil, nil } return ft.decodePrm(d) } func (ft _integerType) decodeRes(d *encoding.Decoder) (interface{}, error) { if !d.Bool() { //null value return nil, nil } return ft.decodePrm(d) } func (ft _bigintType) decodeRes(d *encoding.Decoder) (interface{}, error) { if !d.Bool() { //null value return nil, nil } return ft.decodePrm(d) } func (_realType) decode(d *encoding.Decoder) (interface{}, error) { v := d.Uint32() if v == realNullValue { return nil, nil } return float64(math.Float32frombits(v)), nil } func (_doubleType) decode(d *encoding.Decoder) (interface{}, error) { v := d.Uint64() if v == doubleNullValue { return nil, nil } return math.Float64frombits(v), nil } func (_dateType) decode(d *encoding.Decoder) (interface{}, error) { year, month, day, null := decodeDate(d) if null { return nil, nil } return time.Date(int(year), time.Month(month), int(day), 0, 0, 0, 0, time.UTC), nil } func (_timeType) decode(d *encoding.Decoder) (interface{}, error) { // time read gives only seconds (cut), no milliseconds hour, minute, nanosecs, null := decodeTime(d) if null { return nil, nil } return time.Date(1, 1, 1, hour, minute, 0, nanosecs, time.UTC), nil } func (_timestampType) decode(d *encoding.Decoder) (interface{}, error) { year, month, day, dateNull := decodeDate(d) hour, minute, nanosecs, timeNull := decodeTime(d) if dateNull || timeNull { return nil, nil } return time.Date(year, month, day, hour, minute, 0, nanosecs, time.UTC), 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 decodeDate(d *encoding.Decoder) (int, time.Month, int, bool) { year := d.Uint16() null := ((year & 0x8000) == 0) //null value year &= 0x3fff month := d.Int8() month++ day := d.Int8() return int(year), time.Month(month), int(day), null } func decodeTime(d *encoding.Decoder) (int, int, int, bool) { hour := d.Byte() null := (hour & 0x80) == 0 //null value hour &= 0x7f minute := d.Int8() millisecs := d.Uint16() nanosecs := int(millisecs) * 1000000 return int(hour), int(minute), nanosecs, null } func (_longdateType) decode(d *encoding.Decoder) (interface{}, error) { longdate := d.Int64() if longdate == longdateNullValue { return nil, nil } return convertLongdateToTime(longdate), nil } func (_seconddateType) decode(d *encoding.Decoder) (interface{}, error) { seconddate := d.Int64() if seconddate == seconddateNullValue { return nil, nil } return convertSeconddateToTime(seconddate), nil } func (_daydateType) decode(d *encoding.Decoder) (interface{}, error) { daydate := d.Int32() if daydate == daydateNullValue { return nil, nil } return convertDaydateToTime(int64(daydate)), nil } func (_secondtimeType) decode(d *encoding.Decoder) (interface{}, error) { secondtime := d.Int32() if secondtime == secondtimeNullValue { return nil, nil } return convertSecondtimeToTime(int(secondtime)), nil } func (_decimalType) decode(d *encoding.Decoder) (interface{}, error) { b := make([]byte, decimalFieldSize) d.Bytes(b) if (b[15] & 0x70) == 0x70 { //null value (bit 4,5,6 set) return nil, nil } return b, nil } func (_varType) decode(d *encoding.Decoder) (interface{}, error) { size, null := decodeVarBytesSize(d) if null { return nil, nil } b := make([]byte, size) d.Bytes(b) return b, nil } func (_alphaType) decode(d *encoding.Decoder) (interface{}, error) { size, null := decodeVarBytesSize(d) if null { return nil, nil } switch d.Dfv() { case dfvLevel1: // like _varType b := make([]byte, size) d.Bytes(b) return b, nil default: /* byte: - high bit set -> numeric - high bit unset -> alpha - bits 0-6: field size */ d.Byte() // ignore for the moment b := make([]byte, size-1) d.Bytes(b) return b, nil } } func (_cesu8Type) decode(d *encoding.Decoder) (interface{}, error) { size, null := decodeVarBytesSize(d) if null { return nil, nil } return d.CESU8Bytes(size), nil } func decodeVarBytesSize(d *encoding.Decoder) (int, bool) { ind := d.Byte() //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(d.Int16()), false case ind == bytesLenIndBig: return int(d.Int32()), false } } func decodeLobPrm(d *encoding.Decoder) (interface{}, error) { descr := &lobInDescr{} descr.opt = lobOptions(d.Byte()) descr.size = d.Int32() descr.pos = d.Int32() return nil, nil } // sniffer func (_lobVarType) decodePrm(d *encoding.Decoder) (interface{}, error) { return decodeLobPrm(d) } func (_lobCESU8Type) decodePrm(d *encoding.Decoder) (interface{}, error) { return decodeLobPrm(d) } func decodeLobRes(d *encoding.Decoder, isCharBased bool) (interface{}, error) { descr := &lobOutDescr{isCharBased: isCharBased} descr.ltc = lobTypecode(d.Int8()) descr.opt = lobOptions(d.Int8()) if descr.opt.isNull() { return nil, nil } d.Skip(2) descr.numChar = d.Int64() descr.numByte = d.Int64() descr.id = locatorID(d.Uint64()) size := int(d.Int32()) descr.b = make([]byte, size) d.Bytes(descr.b) return descr, nil } func (_lobVarType) decodeRes(d *encoding.Decoder) (interface{}, error) { return decodeLobRes(d, false) } func (_lobCESU8Type) decodeRes(d *encoding.Decoder) (interface{}, error) { return decodeLobRes(d, true) } golang-github-sap-go-hdb-0.100.10/internal/protocol/functioncode.go000066400000000000000000000035331370256154600251000ustar00rootroot00000000000000/* 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 //nolint 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 (fc functionCode) isProcedureCall() bool { return fc == fcDBProcedureCall } golang-github-sap-go-hdb-0.100.10/internal/protocol/functioncode_string.go000066400000000000000000000027111370256154600264630ustar00rootroot00000000000000// Code generated by "stringer -type=functionCode"; DO NOT EDIT. package protocol import "strconv" func _() { // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. var x [1]struct{} _ = x[fcNil-0] _ = x[fcDDL-1] _ = x[fcInsert-2] _ = x[fcUpdate-3] _ = x[fcDelete-4] _ = x[fcSelect-5] _ = x[fcSelectForUpdate-6] _ = x[fcExplain-7] _ = x[fcDBProcedureCall-8] _ = x[fcDBProcedureCallWithResult-9] _ = x[fcFetch-10] _ = x[fcCommit-11] _ = x[fcRollback-12] _ = x[fcSavepoint-13] _ = x[fcConnect-14] _ = x[fcWriteLob-15] _ = x[fcReadLob-16] _ = x[fcPing-17] _ = x[fcDisconnect-18] _ = x[fcCloseCursor-19] _ = x[fcFindLob-20] _ = x[fcAbapStream-21] _ = x[fcXAStart-22] _ = x[fcXAJoin-23] } 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.100.10/internal/protocol/init.go000066400000000000000000000052561370256154600233670ustar00rootroot00000000000000/* 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/protocol/encoding" ) const ( okEndianess int8 = 1 ) const ( initRequestFillerSize = 4 ) var initRequestFiller uint32 = 0xffffffff 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 (r *initRequest) String() string { switch r.numOptions { default: return fmt.Sprintf("productVersion %s protocolVersion %s", r.product, r.protocol) case 1: return fmt.Sprintf("productVersion %s protocolVersion %s endianess %s", r.product, r.protocol, r.endianess) } } func (r *initRequest) decode(dec *encoding.Decoder) error { dec.Skip(initRequestFillerSize) //filler r.product.major = dec.Int8() r.product.minor = dec.Int16() r.protocol.major = dec.Int8() r.protocol.minor = dec.Int16() dec.Skip(1) //reserved filler r.numOptions = dec.Int8() switch r.numOptions { default: plog.Fatalf("invalid number of options %d", r.numOptions) case 0: dec.Skip(2) case 1: cnt := dec.Int8() if cnt != 1 { plog.Fatalf("endianess %d - 1 expected", cnt) } r.endianess = endianess(dec.Int8()) } return dec.Error() } func (r *initRequest) encode(enc *encoding.Encoder) error { enc.Uint32(initRequestFiller) enc.Int8(r.product.major) enc.Int16(r.product.minor) enc.Int8(r.protocol.major) enc.Int16(r.protocol.minor) switch r.numOptions { default: plog.Fatalf("invalid number of options %d", r.numOptions) case 0: enc.Zeroes(4) case 1: // reserved enc.Zeroes(1) enc.Int8(r.numOptions) enc.Int8(okEndianess) enc.Int8(int8(r.endianess)) } return nil } type initReply struct { product version protocol version } func (r *initReply) String() string { return fmt.Sprintf("productVersion %s protocolVersion %s", r.product, r.protocol) } func (r *initReply) decode(dec *encoding.Decoder) error { r.product.major = dec.Int8() r.product.minor = dec.Int16() r.protocol.major = dec.Int8() r.protocol.minor = dec.Int16() dec.Skip(2) //commitInitReplySize return dec.Error() } golang-github-sap-go-hdb-0.100.10/internal/protocol/julian.go000066400000000000000000000034521370256154600237020ustar00rootroot00000000000000/* 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.100.10/internal/protocol/julian_test.go000066400000000000000000000041151370256154600247360ustar00rootroot00000000000000/* 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{ {1721424, time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC)}, {1842713, time.Date(333, time.January, 27, 0, 0, 0, 0, time.UTC)}, {2299160, time.Date(1582, time.October, 4, 0, 0, 0, 0, time.UTC)}, {2299161, time.Date(1582, time.October, 15, 0, 0, 0, 0, time.UTC)}, {2415021, time.Date(1900, time.January, 1, 0, 0, 0, 0, time.UTC)}, {2447893, time.Date(1990, time.January, 1, 0, 0, 0, 0, time.UTC)}, {2451545, time.Date(2000, time.January, 1, 0, 0, 0, 0, time.UTC)}, {2453750, time.Date(2006, time.January, 14, 0, 0, 0, 0, time.UTC)}, {2455281, time.Date(2010, time.March, 25, 0, 0, 0, 0, time.UTC)}, {2457188, time.Date(2015, time.June, 14, 0, 0, 0, 0, time.UTC)}, {2440587, time.Date(1969, time.December, 31, 0, 0, 0, 0, time.UTC)}, {2440588, time.Date(1970, time.January, 1, 0, 0, 0, 0, time.UTC)}, {5373484, time.Date(9999, time.December, 31, 0, 0, 0, 0, time.UTC)}, {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.100.10/internal/protocol/lob.go000066400000000000000000000155721370256154600232020ustar00rootroot00000000000000/* 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" "github.com/SAP/go-hdb/internal/protocol/encoding" ) const ( locatorIDSize = 8 writeLobRequestSize = 21 ) // 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 (o lobOptions) String() string { t := make([]string, 0, len(lobOptionsText)) for option, text := range lobOptionsText { if (o & option) != 0 { t = append(t, text) } } return fmt.Sprintf("%v", t) } func (o lobOptions) isLastData() bool { return (o & loLastdata) != 0 } func (o lobOptions) isNull() bool { return (o & loNullindicator) != 0 } //go:generate stringer -type=lobTypecode // lob typecode type lobTypecode int8 const ( ltcUndefined lobTypecode = 0 ltcBlob lobTypecode = 1 ltcClob lobTypecode = 2 ltcNclob lobTypecode = 3 ) // not used // type lobFlags bool // func (f lobFlags) String() string { return fmt.Sprintf("%t", f) } // func (f *lobFlags) decode(dec *encoding.Decoder, ph *partHeader) error { // *f = lobFlags(dec.Bool()) // return dec.Error() // } // func (f lobFlags) encode(enc *encoding.Encoder) error { enc.Bool(bool(f)); return nil } // WriterSetter is the interface wrapping the SetWriter method (Lob handling). type WriterSetter interface{ SetWriter(w io.Writer) error } // sessionSetter is the interface wrapping the setSession method (lob handling). type sessionSetter interface{ setSession(s *Session) } var _ WriterSetter = (*lobOutDescr)(nil) var _ sessionSetter = (*lobOutDescr)(nil) /* TODO description lobOutDescr */ type lobInDescr struct { /* currently no data is transformed for input parameters --> opt == 0 (no data included) --> size == 0 --> pos == 0 --> b == nil */ opt lobOptions size int32 pos int32 b []byte // currently no data is transformed for input parameters } /* TODO description lobOutDescr */ type lobOutDescr struct { s *Session isCharBased bool /* HDB does not return lob type code but undefined only --> ltc is always ltcUndefined --> use isCharBased instead of type code check */ ltc lobTypecode opt lobOptions numChar int64 numByte int64 id locatorID b []byte } func (d *lobOutDescr) String() string { return fmt.Sprintf("typecode %s options %s numChar %d numByte %d id %d bytes %v", d.ltc, d.opt, d.numChar, d.numByte, d.id, d.b) } func (d *lobOutDescr) setSession(s *Session) { d.s = s } // SetWriter implements the WriterSetter interface. func (d *lobOutDescr) SetWriter(wr io.Writer) error { return d.s.decodeLobs(d, wr) } /* write lobs: - write lob field to database in chunks - loop: - writeLobRequest - writeLobReply */ // descriptor for writes (lob -> db) type writeLobDescr struct { id locatorID opt lobOptions ofs int64 b []byte } func (d writeLobDescr) String() string { return fmt.Sprintf("id %d options %s offset %d bytes %v", d.id, d.opt, d.ofs, d.b) } // sniffer func (d *writeLobDescr) decode(dec *encoding.Decoder) error { d.id = locatorID(dec.Uint64()) d.opt = lobOptions(dec.Int8()) d.ofs = dec.Int64() size := dec.Int32() d.b = make([]byte, size) dec.Bytes(d.b) return nil } // write chunk to db func (d *writeLobDescr) encode(enc *encoding.Encoder) error { enc.Uint64(uint64(d.id)) enc.Int8(int8(d.opt)) enc.Int64(d.ofs) enc.Int32(int32(len(d.b))) enc.Bytes(d.b) return nil } // write lob fields to db (request) type writeLobRequest struct { descrs []*writeLobDescr } func (r *writeLobRequest) String() string { return fmt.Sprintf("descriptors %v", r.descrs) } func (r *writeLobRequest) size() int { size := 0 for _, descr := range r.descrs { size += (writeLobRequestSize + len(descr.b)) } return size } func (r *writeLobRequest) numArg() int { return len(r.descrs) } // sniffer func (r *writeLobRequest) decode(dec *encoding.Decoder, ph *partHeader) error { numArg := ph.numArg() r.descrs = make([]*writeLobDescr, numArg) for i := 0; i < numArg; i++ { r.descrs[i] = &writeLobDescr{} if err := r.descrs[i].decode(dec); err != nil { return err } } return nil } func (r *writeLobRequest) encode(enc *encoding.Encoder) error { for _, descr := range r.descrs { if err := descr.encode(enc); err != nil { return err } } return nil } // write lob fields to db (reply) // - returns ids which have not been written completely type writeLobReply struct { ids []locatorID } func (r *writeLobReply) String() string { return fmt.Sprintf("ids %v", r.ids) } func (r *writeLobReply) reset(numArg int) { if r.ids == nil || cap(r.ids) < numArg { r.ids = make([]locatorID, numArg) } else { r.ids = r.ids[:numArg] } } func (r *writeLobReply) decode(dec *encoding.Decoder, ph *partHeader) error { numArg := ph.numArg() r.reset(numArg) for i := 0; i < numArg; i++ { r.ids[i] = locatorID(dec.Uint64()) } return dec.Error() } /* read lobs: - read lob field from database in chunks - loop: - readLobRequest - readLobReply - read lob reply seems like readLobreply returns only a result for one lob - even if more then one is requested --> read single lobs */ type readLobRequest struct { id locatorID ofs int64 chunkSize int32 } func (r *readLobRequest) String() string { return fmt.Sprintf("id %d offset %d size %d", r.id, r.ofs, r.chunkSize) } // sniffer func (r *readLobRequest) decode(dec *encoding.Decoder, ph *partHeader) error { r.id = locatorID(dec.Uint64()) r.ofs = dec.Int64() r.chunkSize = dec.Int32() dec.Skip(4) return nil } func (r *readLobRequest) encode(enc *encoding.Encoder) error { enc.Uint64(uint64(r.id)) enc.Int64(r.ofs + 1) //1-based enc.Int32(r.chunkSize) enc.Zeroes(4) return nil } type readLobReply struct { id locatorID opt lobOptions b []byte } func (r *readLobReply) String() string { return fmt.Sprintf("id %d options %s bytes %v", r.id, r.opt, r.b) } func (r *readLobReply) decode(dec *encoding.Decoder, ph *partHeader) error { if ph.numArg() != 1 { panic("numArg == 1 expected") } r.id = locatorID(dec.Uint64()) r.opt = lobOptions(dec.Int8()) size := int(dec.Int32()) dec.Skip(3) r.b = sizeBuffer(r.b, size) dec.Bytes(r.b) return nil } golang-github-sap-go-hdb-0.100.10/internal/protocol/lobtypecode_string.go000066400000000000000000000013211370256154600263100ustar00rootroot00000000000000// Code generated by "stringer -type=lobTypecode"; DO NOT EDIT. package protocol import "strconv" func _() { // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. var x [1]struct{} _ = x[ltcUndefined-0] _ = x[ltcBlob-1] _ = x[ltcClob-2] _ = x[ltcNclob-3] } const _lobTypecode_name = "ltcUndefinedltcBlobltcClobltcNclob" var _lobTypecode_index = [...]uint8{0, 12, 19, 26, 34} func (i lobTypecode) String() string { if i < 0 || i >= lobTypecode(len(_lobTypecode_index)-1) { return "lobTypecode(" + strconv.FormatInt(int64(i), 10) + ")" } return _lobTypecode_name[_lobTypecode_index[i]:_lobTypecode_index[i+1]] } golang-github-sap-go-hdb-0.100.10/internal/protocol/log.go000066400000000000000000000050071370256154600231770ustar00rootroot00000000000000/* Copyright 2020 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 ( "flag" "fmt" "log" "os" ) const ( pPrefix = "hdb.protocol" ) var ( debug bool trace bool ) //nolint:gochecknoinits func init() { flag.BoolVar(&debug, fmt.Sprintf("%s.debug", pPrefix), false, "enabling hdb protocol debugging mode") flag.BoolVar(&trace, fmt.Sprintf("%s.trace", pPrefix), false, "enabling hdb protocol trace") } type pLogger struct { log *log.Logger } func newPLogger() *pLogger { return &pLogger{ log: log.New(os.Stderr, fmt.Sprintf("%s ", pPrefix), log.Ldate|log.Ltime|log.Lshortfile), } } func (l *pLogger) Printf(format string, v ...interface{}) { l.log.Output(2, fmt.Sprintf(format, v...)) } func (l *pLogger) Fatalf(format string, v ...interface{}) { s := fmt.Sprintf(format, v...) l.log.Output(2, fmt.Sprintf(format, v...)) if debug { panic(s) } os.Exit(1) } var plog = newPLogger() // store os.Stdout // executing test examples will override os.Stdout // and fail consequently if trace output is added var stdout = os.Stdout const ( upStreamPrefix = "→" downStreamPrefix = "←" ) func streamPrefix(upStream bool) string { if upStream { return upStreamPrefix } return downStreamPrefix } type traceLogger interface { Log(v interface{}) } type traceLog struct { prefix string log *log.Logger } func (l *traceLog) Log(v interface{}) { var msg string switch v.(type) { case *initRequest, *initReply: msg = fmt.Sprintf("%sINI %s", l.prefix, v) case *messageHeader: msg = fmt.Sprintf("%sMSG %s", l.prefix, v) case *segmentHeader: msg = fmt.Sprintf(" SEG %s", v) case *partHeader: msg = fmt.Sprintf(" PAR %s", v) default: msg = fmt.Sprintf(" %s", v) } l.log.Output(2, msg) } type noTraceLog struct{} func (l *noTraceLog) Log(v interface{}) {} var noTrace = new(noTraceLog) func newTraceLogger(upStream bool) traceLogger { if !trace { return noTrace } return &traceLog{ prefix: streamPrefix(upStream), log: log.New(stdout, fmt.Sprintf("%s ", pPrefix), log.Ldate|log.Ltime), } } golang-github-sap-go-hdb-0.100.10/internal/protocol/message.go000066400000000000000000000030541370256154600240420ustar00rootroot00000000000000/* 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/protocol/encoding" ) const ( messageHeaderSize = 32 //nolint:varcheck ) //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) encode(enc *encoding.Encoder) error { enc.Int64(h.sessionID) enc.Int32(h.packetCount) enc.Uint32(h.varPartLength) enc.Uint32(h.varPartSize) enc.Int16(h.noOfSegm) enc.Zeroes(10) //messageHeaderSize return nil } func (h *messageHeader) decode(dec *encoding.Decoder) error { h.sessionID = dec.Int64() h.packetCount = dec.Int32() h.varPartLength = dec.Uint32() h.varPartSize = dec.Uint32() h.noOfSegm = dec.Int16() dec.Skip(10) //messageHeaderSize return dec.Error() } golang-github-sap-go-hdb-0.100.10/internal/protocol/messagetype.go000066400000000000000000000030161370256154600247420ustar00rootroot00000000000000/* 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 //nolint 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.100.10/internal/protocol/messagetype_string.go000066400000000000000000000043171370256154600263350ustar00rootroot00000000000000// Code generated by "stringer -type=messageType"; DO NOT EDIT. package protocol import "strconv" func _() { // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. var x [1]struct{} _ = x[mtNil-0] _ = x[mtExecuteDirect-2] _ = x[mtPrepare-3] _ = x[mtAbapStream-4] _ = x[mtXAStart-5] _ = x[mtXAJoin-6] _ = x[mtExecute-13] _ = x[mtWriteLob-16] _ = x[mtReadLob-17] _ = x[mtFindLob-18] _ = x[mtAuthenticate-65] _ = x[mtConnect-66] _ = x[mtCommit-67] _ = x[mtRollback-68] _ = x[mtCloseResultset-69] _ = x[mtDropStatementID-70] _ = x[mtFetchNext-71] _ = x[mtFetchAbsolute-72] _ = x[mtFetchRelative-73] _ = x[mtFetchFirst-74] _ = x[mtFetchLast-75] _ = x[mtDisconnect-77] _ = x[mtExecuteITab-78] _ = x[mtFetchNextITab-79] _ = x[mtInsertNextITab-80] } 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.100.10/internal/protocol/option.go000066400000000000000000000074461370256154600237370ustar00rootroot00000000000000/* 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/protocol/encoding" ) type optBooleanType bool type optIntType int32 type optBigintType int64 type optDoubleType float64 type optStringType []byte type optBinaryStringType []byte func (t optBooleanType) String() string { return fmt.Sprintf("%t", bool(t)) } func (t optIntType) String() string { return fmt.Sprintf("%d", int(t)) } func (t optBigintType) String() string { return fmt.Sprintf("%d", int64(t)) } func (t optDoubleType) String() string { return fmt.Sprintf("%g", float64(t)) } func (t optStringType) String() string { return fmt.Sprintf("%s", string(t)) } func (t optBinaryStringType) String() string { return fmt.Sprintf("%v", []byte(t)) } type multiLineOptions []plainOptions func (o multiLineOptions) size() int { size := 0 for _, m := range o { size += m.size() } return size } func (o *multiLineOptions) reset(size int) { if o == nil || size > cap(*o) { *o = make(multiLineOptions, size) } else { *o = (*o)[:size] } } func (o *multiLineOptions) decode(dec *encoding.Decoder, lineCnt int) { o.reset(lineCnt) for i := 0; i < lineCnt; i++ { m := plainOptions{} (*o)[i] = m cnt := dec.Int16() m.decode(dec, int(cnt)) } } func (o multiLineOptions) encode(enc *encoding.Encoder) { for _, m := range o { enc.Int16(int16(len(m))) m.encode(enc) } } type plainOptions map[connectOption]interface{} func (o plainOptions) size() int { size := 2 * len(o) //option + type for _, v := range o { switch v := v.(type) { default: plog.Fatalf("type %T not implemented", v) case optBooleanType: size++ case optIntType: size += 4 case optBigintType: size += 8 case optDoubleType: size += 8 case optStringType: size += (2 + len(v)) //length int16 + string length case optBinaryStringType: size += (2 + len(v)) //length int16 + string length } } return size } func (o plainOptions) decode(dec *encoding.Decoder, cnt int) { for i := 0; i < cnt; i++ { k := connectOption(dec.Int8()) tc := dec.Byte() switch typeCode(tc) { default: plog.Fatalf("type code %s not implemented", typeCode(tc)) case tcBoolean: o[k] = optBooleanType(dec.Bool()) case tcInteger: o[k] = optIntType(dec.Int32()) case tcBigint: o[k] = optBigintType(dec.Int64()) case tcDouble: o[k] = optDoubleType(dec.Float64()) case tcString: size := dec.Int16() v := make([]byte, size) dec.Bytes(v) o[k] = optStringType(v) case tcBstring: size := dec.Int16() v := make([]byte, size) dec.Bytes(v) o[k] = optBinaryStringType(v) } } } func (o plainOptions) encode(enc *encoding.Encoder) { for k, v := range o { enc.Int8(int8(k)) switch v := v.(type) { default: plog.Fatalf("type %T not implemented", v) case optBooleanType: enc.Int8(int8(tcBoolean)) enc.Bool(bool(v)) case optIntType: enc.Int8(int8(tcInteger)) enc.Int32(int32(v)) case optBigintType: enc.Int8(int8(tcBigint)) enc.Int64(int64(v)) case optDoubleType: enc.Int8(int8(tcDouble)) enc.Float64(float64(v)) case optStringType: enc.Int8(int8(tcString)) enc.Int16(int16(len(v))) enc.Bytes(v) case optBinaryStringType: enc.Int8(int8(tcBstring)) enc.Int16(int16(len(v))) enc.Bytes(v) } } } golang-github-sap-go-hdb-0.100.10/internal/protocol/parameter.go000066400000000000000000000150751370256154600244040ustar00rootroot00000000000000/* Copyright 2014 SAP SE Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package protocol import ( "database/sql/driver" "fmt" "github.com/SAP/go-hdb/internal/protocol/encoding" ) 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) } func newParameterFields(size int) []*parameterField { return make([]*parameterField, size) } // parameterField contains database field attributes for parameters. type parameterField struct { name string parameterOptions parameterOptions tc typeCode mode parameterMode fraction int16 length int16 offset uint32 } 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, ) } func (f *parameterField) Converter() Converter { return f.tc.fieldType() } // TypeName returns the type name of the field. // see https://golang.org/pkg/database/sql/driver/#RowsColumnTypeDatabaseTypeName func (f *parameterField) TypeName() string { return f.tc.typeName() } // ScanType returns the scan type of the field. // see https://golang.org/pkg/database/sql/driver/#RowsColumnTypeScanType func (f *parameterField) ScanType() DataType { return f.tc.dataType() } // 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.name } func (f *parameterField) decode(dec *encoding.Decoder) { f.parameterOptions = parameterOptions(dec.Int8()) f.tc = typeCode(dec.Int8()) f.mode = parameterMode(dec.Int8()) dec.Skip(1) //filler f.offset = dec.Uint32() f.length = dec.Int16() f.fraction = dec.Int16() dec.Skip(4) //filler } // parameter metadata type parameterMetadata struct { parameterFields []*parameterField } func (m *parameterMetadata) String() string { return fmt.Sprintf("parameter forlds %v", m.parameterFields) } func (m *parameterMetadata) decode(dec *encoding.Decoder, ph *partHeader) error { m.parameterFields = newParameterFields(ph.numArg()) names := fieldNames{} for i := 0; i < len(m.parameterFields); i++ { f := new(parameterField) f.decode(dec) m.parameterFields[i] = f names.insert(f.offset) } names.decode(dec) for _, f := range m.parameterFields { f.name = names.name(f.offset) } return dec.Error() } // 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("fields %s len(args) %d args %v", p.inputFields, len(p.args), p.args) } func (p *inputParameters) size() int { size := len(p.args) cnt := len(p.inputFields) for i, arg := range p.args { // mass insert f := p.inputFields[i%cnt] size += prmSize(f.tc, arg) } return size } 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) decode(dec *encoding.Decoder, ph *partHeader) error { // TODO Sniffer //return fmt.Errorf("not implemented") return nil } func (p *inputParameters) encode(enc *encoding.Encoder) error { cnt := len(p.inputFields) for i, arg := range p.args { //mass insert f := p.inputFields[i%cnt] if err := encodePrm(enc, f.tc, arg); err != nil { return err } } return nil } // output parameter type outputParameters struct { outputFields []*parameterField fieldValues []driver.Value } func (p *outputParameters) String() string { return fmt.Sprintf("fields %v values %v", p.outputFields, p.fieldValues) } func (p *outputParameters) decode(dec *encoding.Decoder, ph *partHeader) error { numArg := ph.numArg() cols := len(p.outputFields) p.fieldValues = newFieldValues(numArg * cols) for i := 0; i < numArg; i++ { for j, field := range p.outputFields { var err error if p.fieldValues[i*cols+j], err = decodeRes(dec, field.tc); err != nil { return err } } } return dec.Error() } golang-github-sap-go-hdb-0.100.10/internal/protocol/part.go000066400000000000000000000063551370256154600233730ustar00rootroot00000000000000/* 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" "math" "github.com/SAP/go-hdb/internal/protocol/encoding" ) const ( partHeaderSize = 16 maxPartNum = math.MaxInt16 ) 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("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) setNumArg(numArg int) error { switch { default: return fmt.Errorf("maximum number of arguments %d exceeded", numArg) case numArg <= maxPartNum: h.argumentCount = int16(numArg) h.bigArgumentCount = 0 // TODO: seems not to work: see bulk insert test // case numArg <= math.MaxInt32: // s.ph.argumentCount = 0 // s.ph.bigArgumentCount = int32(numArg) // } return nil } func (h *partHeader) numArg() int { if h.bigArgumentCount != 0 { panic("part header: bigArgumentCount is set") } return int(h.argumentCount) } func (h *partHeader) encode(enc *encoding.Encoder) error { enc.Int8(int8(h.partKind)) enc.Int8(int8(h.partAttributes)) enc.Int16(h.argumentCount) enc.Int32(h.bigArgumentCount) enc.Int32(h.bufferLength) enc.Int32(h.bufferSize) //no filler return nil } func (h *partHeader) decode(dec *encoding.Decoder) error { h.partKind = partKind(dec.Int8()) h.partAttributes = partAttributes(dec.Int8()) h.argumentCount = dec.Int16() h.bigArgumentCount = dec.Int32() h.bufferLength = dec.Int32() h.bufferSize = dec.Int32() // no filler return dec.Error() } golang-github-sap-go-hdb-0.100.10/internal/protocol/partkind.go000066400000000000000000000060371370256154600242360ustar00rootroot00000000000000/* 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 //nolint 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.100.10/internal/protocol/partkind_string.go000066400000000000000000000105451370256154600256230ustar00rootroot00000000000000// Code generated by "stringer -type=partKind"; DO NOT EDIT. package protocol import "strconv" func _() { // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. var x [1]struct{} _ = x[pkNil-0] _ = x[pkCommand-3] _ = x[pkResultset-5] _ = x[pkError-6] _ = x[pkStatementID-10] _ = x[pkTransactionID-11] _ = x[pkRowsAffected-12] _ = x[pkResultsetID-13] _ = x[pkTopologyInformation-15] _ = x[pkTableLocation-16] _ = x[pkReadLobRequest-17] _ = x[pkReadLobReply-18] _ = x[pkAbapIStream-25] _ = x[pkAbapOStream-26] _ = x[pkCommandInfo-27] _ = x[pkWriteLobRequest-28] _ = x[pkClientContext-29] _ = x[pkWriteLobReply-30] _ = x[pkParameters-32] _ = x[pkAuthentication-33] _ = x[pkSessionContext-34] _ = x[pkClientID-35] _ = x[pkProfile-38] _ = x[pkStatementContext-39] _ = x[pkPartitionInformation-40] _ = x[pkOutputParameters-41] _ = x[pkConnectOptions-42] _ = x[pkCommitOptions-43] _ = x[pkFetchOptions-44] _ = x[pkFetchSize-45] _ = x[pkParameterMetadata-47] _ = x[pkResultMetadata-48] _ = x[pkFindLobRequest-49] _ = x[pkFindLobReply-50] _ = x[pkItabSHM-51] _ = x[pkItabChunkMetadata-53] _ = x[pkItabMetadata-55] _ = x[pkItabResultChunk-56] _ = x[pkClientInfo-57] _ = x[pkStreamData-58] _ = x[pkOStreamResult-59] _ = x[pkFDARequestMetadata-60] _ = x[pkFDAReplyMetadata-61] _ = x[pkBatchPrepare-62] _ = x[pkBatchExecute-63] _ = x[pkTransactionFlags-64] _ = x[pkRowSlotImageParamMetadata-65] _ = x[pkRowSlotImageResultset-66] _ = x[pkDBConnectInfo-67] _ = x[pkLobFlags-68] _ = x[pkResultsetOptions-69] _ = x[pkXATransactionInfo-70] _ = x[pkSessionVariable-71] _ = x[pkWorkLoadReplayContext-72] _ = x[pkSQLReplyOptions-73] } 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.100.10/internal/protocol/parts.go000066400000000000000000000211701370256154600235460ustar00rootroot00000000000000/* Copyright 2020 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" "reflect" "github.com/SAP/go-hdb/internal/protocol/encoding" ) type partEncoder interface { size() int encode(*encoding.Encoder) error } type partDecoder interface { decode(*encoding.Decoder, *partHeader) error } type partDecodeEncoder interface { partDecoder partEncoder } // TODO: uncomment after go1.13 compatibility is dropped. // type partReadWriter interface { // partReader // partWriter // } type part interface { String() string // should support Stringer interface kind() partKind } // part kind methods func (*hdbErrors) kind() partKind { return pkError } func (*authInitReq) kind() partKind { return pkAuthentication } func (*authInitRep) kind() partKind { return pkAuthentication } func (*authFinalReq) kind() partKind { return pkAuthentication } func (*authFinalRep) kind() partKind { return pkAuthentication } func (clientID) kind() partKind { return pkClientID } func (connectOptions) kind() partKind { return pkConnectOptions } func (*topologyInformation) kind() partKind { return pkTopologyInformation } func (command) kind() partKind { return pkCommand } func (*rowsAffected) kind() partKind { return pkRowsAffected } func (transactionFlags) kind() partKind { return pkTransactionFlags } func (statementContext) kind() partKind { return pkStatementContext } func (statementID) kind() partKind { return pkStatementID } func (*parameterMetadata) kind() partKind { return pkParameterMetadata } func (*inputParameters) kind() partKind { return pkParameters } func (*outputParameters) kind() partKind { return pkOutputParameters } func (*resultMetadata) kind() partKind { return pkResultMetadata } func (resultsetID) kind() partKind { return pkResultsetID } func (*resultset) kind() partKind { return pkResultset } func (fetchsize) kind() partKind { return pkFetchSize } func (*readLobRequest) kind() partKind { return pkReadLobRequest } func (*readLobReply) kind() partKind { return pkReadLobReply } func (*writeLobRequest) kind() partKind { return pkWriteLobRequest } func (*writeLobReply) kind() partKind { return pkWriteLobReply } // func (lobFlags) kind() partKind { return pkLobFlags } // check if part types implement part interface var ( _ part = (*hdbErrors)(nil) _ part = (*authInitReq)(nil) _ part = (*authInitRep)(nil) _ part = (*authFinalReq)(nil) _ part = (*authFinalRep)(nil) _ part = (*clientID)(nil) _ part = (*connectOptions)(nil) _ part = (*topologyInformation)(nil) _ part = (*command)(nil) _ part = (*rowsAffected)(nil) _ part = (*transactionFlags)(nil) _ part = (*statementContext)(nil) _ part = (*statementID)(nil) _ part = (*parameterMetadata)(nil) _ part = (*inputParameters)(nil) _ part = (*outputParameters)(nil) _ part = (*resultMetadata)(nil) _ part = (*resultsetID)(nil) _ part = (*resultset)(nil) _ part = (*fetchsize)(nil) _ part = (*readLobRequest)(nil) _ part = (*readLobReply)(nil) _ part = (*writeLobRequest)(nil) _ part = (*writeLobReply)(nil) // _ part = (*lobFlags)(nil) ) type partWriter interface { part numArg() int partEncoder } // numArg methods (result == 1) func (*authInitReq) numArg() int { return 1 } func (*authInitRep) numArg() int { return 1 } func (*authFinalReq) numArg() int { return 1 } func (*authFinalRep) numArg() int { return 1 } func (clientID) numArg() int { return 1 } func (command) numArg() int { return 1 } func (statementID) numArg() int { return 1 } func (resultsetID) numArg() int { return 1 } func (fetchsize) numArg() int { return 1 } func (*readLobRequest) numArg() int { return 1 } // func (lobFlags) numArg() int { return 1 } // size methods (fixed size) const ( statementIDSize = 8 resultsetIDSize = 8 fetchsizeSize = 4 readLobRequestSize = 24 ) func (statementID) size() int { return statementIDSize } func (resultsetID) size() int { return resultsetIDSize } func (fetchsize) size() int { return fetchsizeSize } func (readLobRequest) size() int { return readLobRequestSize } // func (lobFlags) size() int { return tinyintFieldSize } // check if part types implement partWriter interface var ( _ partWriter = (*authInitReq)(nil) _ partWriter = (*authFinalReq)(nil) _ partWriter = (*clientID)(nil) _ partWriter = (*connectOptions)(nil) _ partWriter = (*command)(nil) _ partWriter = (*statementID)(nil) _ partWriter = (*inputParameters)(nil) _ partWriter = (*resultsetID)(nil) _ partWriter = (*fetchsize)(nil) _ partReader = (*readLobRequest)(nil) _ partReader = (*writeLobRequest)(nil) // _ partWriter = (*lobFlags)(nil) ) type partReader interface { part partDecoder } // check if part types implement partReader interface var ( _ partReader = (*hdbErrors)(nil) _ partReader = (*authInitReq)(nil) _ partReader = (*authInitRep)(nil) _ partReader = (*authFinalReq)(nil) _ partReader = (*authFinalRep)(nil) _ partReader = (*clientID)(nil) _ partReader = (*connectOptions)(nil) _ partReader = (*topologyInformation)(nil) _ partReader = (*command)(nil) _ partReader = (*rowsAffected)(nil) _ partReader = (*transactionFlags)(nil) _ partReader = (*statementContext)(nil) _ partReader = (*statementID)(nil) _ partReader = (*parameterMetadata)(nil) _ partReader = (*inputParameters)(nil) _ partReader = (*outputParameters)(nil) _ partReader = (*resultMetadata)(nil) _ partReader = (*resultsetID)(nil) _ partReader = (*resultset)(nil) _ partReader = (*fetchsize)(nil) _ partReader = (*readLobRequest)(nil) _ partReader = (*writeLobRequest)(nil) _ partReader = (*readLobReply)(nil) _ partReader = (*writeLobReply)(nil) ) // some partReader needs additional parameter set before reading type prmPartReader interface { partReader prm() // marker interface } // prm marker methods func (*inputParameters) prm() {} func (*outputParameters) prm() {} func (*resultset) prm() {} var ( _ prmPartReader = (*inputParameters)(nil) _ prmPartReader = (*outputParameters)(nil) _ prmPartReader = (*resultset)(nil) ) var partTypeMap = map[partKind]reflect.Type{ pkError: reflect.TypeOf((*hdbErrors)(nil)).Elem(), pkClientID: reflect.TypeOf((*clientID)(nil)).Elem(), pkConnectOptions: reflect.TypeOf((*connectOptions)(nil)).Elem(), pkTopologyInformation: reflect.TypeOf((*topologyInformation)(nil)).Elem(), pkCommand: reflect.TypeOf((*command)(nil)).Elem(), pkRowsAffected: reflect.TypeOf((*rowsAffected)(nil)).Elem(), pkTransactionFlags: reflect.TypeOf((*transactionFlags)(nil)).Elem(), pkStatementContext: reflect.TypeOf((*statementContext)(nil)).Elem(), pkStatementID: reflect.TypeOf((*statementID)(nil)).Elem(), pkParameterMetadata: reflect.TypeOf((*parameterMetadata)(nil)).Elem(), pkParameters: reflect.TypeOf((*inputParameters)(nil)).Elem(), pkOutputParameters: reflect.TypeOf((*outputParameters)(nil)).Elem(), pkResultMetadata: reflect.TypeOf((*resultMetadata)(nil)).Elem(), pkResultsetID: reflect.TypeOf((*resultsetID)(nil)).Elem(), pkResultset: reflect.TypeOf((*resultset)(nil)).Elem(), pkFetchSize: reflect.TypeOf((*fetchsize)(nil)).Elem(), pkReadLobRequest: reflect.TypeOf((*readLobRequest)(nil)).Elem(), pkReadLobReply: reflect.TypeOf((*readLobReply)(nil)).Elem(), pkWriteLobReply: reflect.TypeOf((*writeLobReply)(nil)).Elem(), pkWriteLobRequest: reflect.TypeOf((*writeLobRequest)(nil)).Elem(), } var partReaderType = reflect.TypeOf((*partReader)(nil)).Elem() var prmPartReaderType = reflect.TypeOf((*prmPartReader)(nil)).Elem() func newPartReader(pk partKind) (partReader, error) { pt, ok := partTypeMap[pk] if !ok { return nil, fmt.Errorf("part type map - part kind %s not found", pk) } part := reflect.New(pt).Interface() if _, ok := part.(prmPartReader); ok { return nil, fmt.Errorf("part kind %s does implement parameter part reader interface", pk) } partReader, ok := part.(partReader) if !ok { return nil, fmt.Errorf("part kind %s does not implement part reader interface", pk) } return partReader, nil } golang-github-sap-go-hdb-0.100.10/internal/protocol/parts1.13.go000066400000000000000000000013161370256154600240510ustar00rootroot00000000000000// +build !go1.14 /* Copyright 2020 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 // Delete after go1.13 is out of maintenance. type partReadWriter interface { part numArg() int partDecoder partEncoder } golang-github-sap-go-hdb-0.100.10/internal/protocol/parts1.14.go000066400000000000000000000013251370256154600240520ustar00rootroot00000000000000// +build go1.14 /* Copyright 2020 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. */ // Delete and re-itegrate into parts.go after go1.13 is out of maintenance. package protocol type partReadWriter interface { partReader partWriter } golang-github-sap-go-hdb-0.100.10/internal/protocol/protocol.go000066400000000000000000000365041370256154600242650ustar00rootroot00000000000000/* Copyright 2020 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 ( "bufio" "database/sql/driver" "errors" "fmt" "io" "math" "github.com/SAP/go-hdb/driver/sqltrace" "github.com/SAP/go-hdb/internal/protocol/encoding" ) // rowsResult represents the row resultset of a query or stored procedure (output parameters, call table results). type rowsResult interface { rsID() uint64 // RsID returns the resultset id. columns() []string // Columns returns the names of the resultset columns. numRow() int // NumRow returns the number of rows available in FieldValues. closed() bool // Closed returnr true if the database resultset is closed (completely read). lastPacket() bool // LastPacket returns true if the last packet of a resultset was read from database. copyRow(idx int, dest []driver.Value) // CopyRow fills the dest value slice with row data at index idx. field(idx int) Field // Field returns the field descriptor at position idx. queryResult() (*queryResult, error) // Used by fetch next if RowsResult is based on a query (nil for CallResult). } var ( _ rowsResult = (*queryResult)(nil) _ rowsResult = (*callResult)(nil) ) // A PrepareResult represents the result of a prepare statement. type PrepareResult struct { fc functionCode stmtID uint64 prmFields []*parameterField resultFields []*resultField } // Check checks consistency of the prepare result. func (pr *PrepareResult) Check(qd *QueryDescr) error { call := qd.kind == QkCall if call != pr.fc.isProcedureCall() { return fmt.Errorf("function code mismatch: query descriptor %s - function code %s", qd.kind, pr.fc) } if !call { // only input parameters allowed for _, f := range pr.prmFields { if f.Out() { return fmt.Errorf("invalid parameter %s", f) } } } return nil } // StmtID returns the statement id. func (pr *PrepareResult) StmtID() uint64 { return pr.stmtID } // IsProcedureCall returns true if the statement is a call statement. func (pr *PrepareResult) IsProcedureCall() bool { return pr.fc.isProcedureCall() } // NumField returns the number of parameter fields in a database statement. func (pr *PrepareResult) NumField() int { return len(pr.prmFields) } // NumInputField returns the number of input fields in a database statement. func (pr *PrepareResult) NumInputField() int { if !pr.fc.isProcedureCall() { return len(pr.prmFields) // only input fields } numField := 0 for _, f := range pr.prmFields { if f.In() { numField++ } } return numField } // PrmField returns the parameter field at index idx. func (pr *PrepareResult) PrmField(idx int) Field { return pr.prmFields[idx] } // A QueryResult represents the resultset of a query. type queryResult struct { _rsID uint64 fields []*resultField fieldValues []driver.Value attributes partAttributes _columns []string } // RsID implements the RowsResult interface. func (qr *queryResult) rsID() uint64 { return qr._rsID } // Field implements the RowsResult interface. func (qr *queryResult) field(idx int) Field { return qr.fields[idx] } // NumRow implements the RowsResult interface. func (qr *queryResult) numRow() int { if qr.fieldValues == nil { return 0 } return len(qr.fieldValues) / len(qr.fields) } // CopyRow implements the RowsResult interface. func (qr *queryResult) copyRow(idx int, dest []driver.Value) { cols := len(qr.fields) copy(dest, qr.fieldValues[idx*cols:(idx+1)*cols]) } // Closed implements the RowsResult interface. func (qr *queryResult) closed() bool { return qr.attributes.ResultsetClosed() } // LastPacket implements the RowsResult interface. func (qr *queryResult) lastPacket() bool { return qr.attributes.LastPacket() } // Columns implements the RowsResult interface. func (qr *queryResult) columns() []string { if qr._columns == nil { numField := len(qr.fields) qr._columns = make([]string, numField) for i := 0; i < numField; i++ { qr._columns[i] = qr.fields[i].Name() } } return qr._columns } func (qr *queryResult) queryResult() (*queryResult, error) { return qr, nil } // A CallResult represents the result (output parameters and values) of a call statement. type callResult struct { // call output parameters outputFields []*parameterField fieldValues []driver.Value _columns []string qrs []*queryResult // table output parameters } // RsID implements the RowsResult interface. func (cr *callResult) rsID() uint64 { return 0 } // Field implements the RowsResult interface. func (cr *callResult) field(idx int) Field { return cr.outputFields[idx] } // NumRow implements the RowsResult interface. func (cr *callResult) numRow() int { if cr.fieldValues == nil { return 0 } return len(cr.fieldValues) / len(cr.outputFields) } // CopyRow implements the RowsResult interface. func (cr *callResult) copyRow(idx int, dest []driver.Value) { cols := len(cr.outputFields) copy(dest, cr.fieldValues[idx*cols:(idx+1)*cols]) } // Closed implements the RowsResult interface. func (cr *callResult) closed() bool { return true } // LastPacket implements the RowsResult interface. func (cr *callResult) lastPacket() bool { return true } // Columns implements the RowsResult interface. func (cr *callResult) columns() []string { if cr._columns == nil { numField := len(cr.outputFields) cr._columns = make([]string, numField) for i := 0; i < numField; i++ { cr._columns[i] = cr.outputFields[i].Name() } } return cr._columns } func (cr *callResult) queryResult() (*queryResult, error) { return nil, errors.New("cannot use call result as query result") } func (cr *callResult) appendTableRefFields() { for i, qr := range cr.qrs { cr.outputFields = append(cr.outputFields, ¶meterField{name: fmt.Sprintf("table %d", i), tc: tcTableRef, mode: pmOut, offset: 0}) cr.fieldValues = append(cr.fieldValues, encodeID(qr._rsID)) } } func (cr *callResult) appendTableRowsFields(s *Session) { for i, qr := range cr.qrs { cr.outputFields = append(cr.outputFields, ¶meterField{name: fmt.Sprintf("table %d", i), tc: tcTableRows, mode: pmOut, offset: 0}) cr.fieldValues = append(cr.fieldValues, newQueryResultSet(s, qr)) } } type protocolReader struct { upStream bool // authentication step int method string dec *encoding.Decoder tracer traceLogger mh *messageHeader sh *segmentHeader ph *partHeader msgSize int64 numPart int cntPart int partRead bool partReaderCache map[partKind]partReader lastErrors *hdbErrors lastRowsAffected *rowsAffected // partReader read errors could be // - read buffer errors -> buffer Error() and ResetError() // - plus other errors (which cannot be ignored, e.g. Lob reader) err error } func newProtocolReader(upStream bool, rd io.Reader) *protocolReader { return &protocolReader{ upStream: upStream, dec: encoding.NewDecoder(rd), tracer: newTraceLogger(upStream), partReaderCache: map[partKind]partReader{}, mh: &messageHeader{}, sh: &segmentHeader{}, ph: &partHeader{}, } } func (r *protocolReader) setDfv(dfv int) { r.dec.SetDfv(dfv) } func (r *protocolReader) readSkip() error { return r.iterateParts(nil) } func (r *protocolReader) sessionID() int64 { return r.mh.sessionID } func (r *protocolReader) functionCode() functionCode { return r.sh.functionCode } func (r *protocolReader) readInitRequest() error { req := &initRequest{} if err := req.decode(r.dec); err != nil { return err } r.tracer.Log(req) return nil } func (r *protocolReader) readInitReply() error { rep := &initReply{} if err := rep.decode(r.dec); err != nil { return err } r.tracer.Log(rep) return nil } func (r *protocolReader) readProlog() error { if r.upStream { return r.readInitRequest() } return r.readInitReply() } func (r *protocolReader) checkError() error { defer func() { // init readFlags r.lastErrors = nil r.lastRowsAffected = nil r.err = nil r.dec.ResetError() }() if r.err != nil { return r.err } if err := r.dec.Error(); err != nil { return err } if r.lastErrors == nil { return nil } if r.lastRowsAffected != nil { // link statement to error j := 0 for i, rows := range *r.lastRowsAffected { if rows == raExecutionFailed { r.lastErrors.setStmtNo(j, i) j++ } } } if r.lastErrors.isWarnings() { for _, e := range r.lastErrors.errors { sqltrace.Traceln(e) } return nil } return r.lastErrors } func (r *protocolReader) canSkip(pk partKind) bool { // errors and rowsAffected needs always to be read if pk == pkError || pk == pkRowsAffected { return false } if debug { return false } return true } func (r *protocolReader) read(part partReader) error { r.partRead = true err := r.readPart(part) if err != nil { r.err = err } switch part := part.(type) { case *hdbErrors: r.lastErrors = part case *rowsAffected: r.lastRowsAffected = part } return err } func (r *protocolReader) authPart() partReader { defer func() { r.step++ }() switch { case r.upStream && r.step == 0: return &authInitReq{} case r.upStream: return &authFinalReq{} case !r.upStream && r.step == 0: return &authInitRep{} case !r.upStream: return &authFinalRep{} default: panic(fmt.Errorf("invalid auth step in protocol reader %d", r.step)) } } func (r *protocolReader) defaultPart(pk partKind) (partReader, error) { part, ok := r.partReaderCache[pk] if !ok { var err error part, err = newPartReader(pk) if err != nil { return nil, err } r.partReaderCache[pk] = part } return part, nil } func (r *protocolReader) skip() error { pk := r.ph.partKind if r.canSkip(pk) { return r.skipPart() } var part partReader var err error if pk == pkAuthentication { part = r.authPart() } else { part, err = r.defaultPart(pk) } if err != nil { return r.skipPart() } return r.read(part) } func (r *protocolReader) skipPart() error { r.dec.Skip(int(r.ph.bufferLength)) r.tracer.Log("*skipped") /* hdb protocol - in general padding but - in some messages the last record sent is not padded - message header varPartLength < segment header segmentLength - msgSize == 0: mh.varPartLength == sh.segmentLength - msgSize < 0 : mh.varPartLength < sh.segmentLength */ if r.cntPart != r.numPart || r.msgSize == 0 { r.dec.Skip(padBytes(int(r.ph.bufferLength))) } return nil } func (r *protocolReader) readPart(part partReader) error { r.dec.ResetCnt() if err := part.decode(r.dec, r.ph); err != nil { return err // do not ignore partReader errros } cnt := r.dec.Cnt() r.tracer.Log(part) bufferLen := int(r.ph.bufferLength) switch { case cnt < bufferLen: // protocol buffer length > read bytes -> skip the unread bytes // TODO enable for debug // b := make([]byte, bufferLen-cnt) // p.rd.ReadFull(b) // println(fmt.Sprintf("%x", b)) // println(string(b)) r.dec.Skip(bufferLen - cnt) case cnt > bufferLen: // read bytes > protocol buffer length -> should never happen panic(fmt.Errorf("protocol error: read bytes %d > buffer length %d", cnt, bufferLen)) } /* hdb protocol - in general padding but - in some messages the last record sent is not padded - message header varPartLength < segment header segmentLength - msgSize == 0: mh.varPartLength == sh.segmentLength - msgSize < 0 : mh.varPartLength < sh.segmentLength */ if r.cntPart != r.numPart || r.msgSize == 0 { r.dec.Skip(padBytes(int(r.ph.bufferLength))) } return nil } func (r *protocolReader) iterateParts(partCb func(ph *partHeader)) error { if err := r.mh.decode(r.dec); err != nil { return err } r.tracer.Log(r.mh) r.msgSize = int64(r.mh.varPartLength) for i := 0; i < int(r.mh.noOfSegm); i++ { if err := r.sh.decode(r.dec); err != nil { return err } r.tracer.Log(r.sh) r.msgSize -= int64(r.sh.segmentLength) r.numPart = int(r.sh.noOfParts) r.cntPart = 0 for j := 0; j < int(r.sh.noOfParts); j++ { if err := r.ph.decode(r.dec); err != nil { return err } r.tracer.Log(r.ph) r.cntPart++ r.partRead = false if partCb != nil { partCb(r.ph) } if !r.partRead { r.skip() } if r.err != nil { return r.err } } } return r.checkError() } // protocol writer type protocolWriter struct { wr *bufio.Writer enc *encoding.Encoder tracer traceLogger // reuse header mh *messageHeader sh *segmentHeader ph *partHeader } func newProtocolWriter(wr *bufio.Writer) *protocolWriter { return &protocolWriter{ wr: wr, enc: encoding.NewEncoder(wr), tracer: newTraceLogger(true), mh: new(messageHeader), sh: new(segmentHeader), ph: new(partHeader), } } const ( productVersionMajor = 4 productVersionMinor = 20 protocolVersionMajor = 4 protocolVersionMinor = 1 ) func (w *protocolWriter) writeProlog() error { req := &initRequest{} req.product.major = productVersionMajor req.product.minor = productVersionMinor req.protocol.major = protocolVersionMajor req.protocol.minor = protocolVersionMinor req.numOptions = 1 req.endianess = littleEndian if err := req.encode(w.enc); err != nil { return err } w.tracer.Log(req) return w.wr.Flush() } func (w *protocolWriter) write(sessionID int64, messageType messageType, commit bool, writers ...partWriter) error { numWriters := len(writers) partSize := make([]int, numWriters) size := int64(segmentHeaderSize + numWriters*partHeaderSize) //int64 to hold MaxUInt32 in 32bit OS for i, part := range writers { s := part.size() 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 w.mh.sessionID = sessionID w.mh.varPartLength = uint32(size) w.mh.varPartSize = uint32(bufferSize) w.mh.noOfSegm = 1 if err := w.mh.encode(w.enc); err != nil { return err } w.tracer.Log(w.mh) if size > math.MaxInt32 { return fmt.Errorf("message size %d exceeds maximum part header value %d", size, math.MaxInt32) } w.sh.messageType = messageType w.sh.commit = commit w.sh.segmentKind = skRequest w.sh.segmentLength = int32(size) w.sh.segmentOfs = 0 w.sh.noOfParts = int16(numWriters) w.sh.segmentNo = 1 if err := w.sh.encode(w.enc); err != nil { return err } w.tracer.Log(w.sh) bufferSize -= segmentHeaderSize for i, part := range writers { size := partSize[i] pad := padBytes(size) w.ph.partKind = part.kind() if err := w.ph.setNumArg(part.numArg()); err != nil { return err } w.ph.bufferLength = int32(size) w.ph.bufferSize = int32(bufferSize) if err := w.ph.encode(w.enc); err != nil { return err } w.tracer.Log(w.ph) if err := part.encode(w.enc); err != nil { return err } w.tracer.Log(part) w.enc.Zeroes(pad) bufferSize -= int64(partHeaderSize + size + pad) } return w.wr.Flush() } golang-github-sap-go-hdb-0.100.10/internal/protocol/querydescr.go000066400000000000000000000065301370256154600246060ustar00rootroot00000000000000/* Copyright 2020 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 ( "errors" "fmt" "strconv" "strings" "github.com/SAP/go-hdb/internal/protocol/scanner" ) // QueryKind is the query type of a database statement. type QueryKind int func (k QueryKind) String() string { keyword, ok := queryKindKeyword[k] if ok { return keyword } return fmt.Sprintf("cmdKind(%d)", k) } // Query kind constants. const ( QkUnknown QueryKind = iota QkCall QkSelect QkInsert QkUpdate QkUpsert QkCreate QkDrop QkSet QkID ) var ( queryKindKeyword = map[QueryKind]string{ QkUnknown: "unknown", QkCall: "call", QkSelect: "select", QkInsert: "insert", QkUpdate: "update", QkUpsert: "upsert", QkCreate: "create", QkDrop: "drop", QkSet: "set", QkID: "id", } queryKeywordKind = map[string]QueryKind{} ) func init() { // build cmdKeywordKind from cmdKindKeyword for k, v := range queryKindKeyword { queryKeywordKind[v] = k } } func encodeID(id uint64) string { return fmt.Sprintf("%s %s", queryKindKeyword[QkID], strconv.FormatUint(id, 10)) } var errInvalidCmdToken = errors.New("invalid command token") const ( bulkQuery = "bulk" ) // QueryDescr represents a query descriptor of a database statement. type QueryDescr struct { query string kind QueryKind isBulk bool id uint64 } func (d *QueryDescr) String() string { return fmt.Sprintf("query: %s kind: %s isBulk: %t", d.query, d.kind, d.isBulk) } // Query return the query statement of a query descriptor. func (d *QueryDescr) Query() string { return d.query } // Kind return the query kind of a query descriptor. func (d *QueryDescr) Kind() QueryKind { return d.kind } // ID return the query id of a query descriptor (legacy mode: call table output parameters). func (d *QueryDescr) ID() uint64 { return d.id } // IsBulk returns true if the query is a bulk statement.. func (d *QueryDescr) IsBulk() bool { return d.isBulk } // NewQueryDescr returns a new QueryDescr instance. func NewQueryDescr(query string, sc *scanner.Scanner) (*QueryDescr, error) { d := &QueryDescr{query: query} sc.Reset(query) // first token token, start, end := sc.Next() if token != scanner.Identifier { return nil, errInvalidCmdToken } if strings.ToLower(query[start:end]) == bulkQuery { d.isBulk = true _, start, end = sc.Next() } // kind keyword := strings.ToLower(query[start:end]) d.kind = QkUnknown kind, ok := queryKeywordKind[keyword] if ok { d.kind = kind } // command d.query = query[start:] // cut off whitespaces and bulk // result set id query if d.kind == QkID { token, start, end = sc.Next() if token != scanner.Number { return nil, errInvalidCmdToken } var err error d.id, err = strconv.ParseUint(query[start:end], 10, 64) if err != nil { return nil, err } } // TODO release v1.0.0 - scan variables (named parameters) return d, nil } golang-github-sap-go-hdb-0.100.10/internal/protocol/queryresultset.go000066400000000000000000000115441370256154600255410ustar00rootroot00000000000000/* 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" "io" "reflect" "sync" ) /* Definition of queryResultSet in protocol layer: - queryResultSet is sql.Rows - sql.Rows can be used as datatype for scan - used ig go-hdb for call table output parameters */ // NoResult is the driver.Rows drop-in replacement if driver Query or QueryRow is used for statements that do not return rows. var noResult = new(noResultType) // check if noResultType implements all required interfaces var ( _ driver.Rows = (*noResultType)(nil) ) var noColumns = []string{} type noResultType struct{} func (r *noResultType) Columns() []string { return noColumns } func (r *noResultType) Close() error { return nil } func (r *noResultType) Next(dest []driver.Value) error { return io.EOF } // query result set // check if queryResult implements all required interfaces var ( _ driver.Rows = (*queryResultSet)(nil) _ driver.RowsColumnTypeDatabaseTypeName = (*queryResultSet)(nil) // go 1.8 _ driver.RowsColumnTypeLength = (*queryResultSet)(nil) // go 1.8 _ driver.RowsColumnTypeNullable = (*queryResultSet)(nil) // go 1.8 _ driver.RowsColumnTypePrecisionScale = (*queryResultSet)(nil) // go 1.8 _ driver.RowsColumnTypeScanType = (*queryResultSet)(nil) // go 1.8 _ driver.RowsNextResultSet = (*queryResultSet)(nil) // go 1.8 ) type queryResultSet struct { session *Session rrs []rowsResult rr rowsResult idx int // current result set pos int lastErr error } func newQueryResultSet(session *Session, rrs ...rowsResult) *queryResultSet { if len(rrs) == 0 { panic("query result set is empty") } return &queryResultSet{session: session, rrs: rrs, rr: rrs[0]} } func (r *queryResultSet) Columns() []string { return r.rr.columns() } func (r *queryResultSet) Close() error { r.session.Lock() defer r.session.Unlock() defer r.session.SetInQuery(false) // if lastError is set, attrs are nil if r.lastErr != nil { return r.lastErr } if !r.rr.closed() { return r.session.CloseResultsetID(r.rr.rsID()) } return nil } func (r *queryResultSet) Next(dest []driver.Value) error { r.session.Lock() defer r.session.Unlock() if r.session.IsBad() { return driver.ErrBadConn } if r.pos >= r.rr.numRow() { if r.rr.lastPacket() { return io.EOF } if err := r.session.fetchNext(r.rr); err != nil { r.lastErr = err //fieldValues and attrs are nil return err } if r.rr.numRow() == 0 { return io.EOF } r.pos = 0 } r.rr.copyRow(r.pos, dest) r.pos++ // TODO eliminate for _, v := range dest { if v, ok := v.(sessionSetter); ok { v.setSession(r.session) } } return nil } func (r *queryResultSet) HasNextResultSet() bool { return (r.idx + 1) < len(r.rrs) } func (r *queryResultSet) NextResultSet() error { if !r.HasNextResultSet() { return io.EOF } r.lastErr = nil r.idx++ r.rr = r.rrs[r.idx] return nil } func (r *queryResultSet) ColumnTypeDatabaseTypeName(idx int) string { return r.rr.field(idx).TypeName() } func (r *queryResultSet) ColumnTypeLength(idx int) (int64, bool) { return r.rr.field(idx).TypeLength() } func (r *queryResultSet) ColumnTypePrecisionScale(idx int) (int64, int64, bool) { return r.rr.field(idx).TypePrecisionScale() } func (r *queryResultSet) ColumnTypeNullable(idx int) (bool, bool) { return r.rr.field(idx).Nullable(), true } func (r *queryResultSet) ColumnTypeScanType(idx int) reflect.Type { return scanTypeMap[r.rr.field(idx).ScanType()] } // QrsCache is a query result cache supporting reading // procedure (call) table parameter via separate query (legacy mode). var QrsCache = newQueryResultSetCache() type queryResultSetCache struct { cache map[uint64]*queryResultSet mu sync.RWMutex } func newQueryResultSetCache() *queryResultSetCache { return &queryResultSetCache{ cache: map[uint64]*queryResultSet{}, } } func (c *queryResultSetCache) set(id uint64, qrs *queryResultSet) uint64 { c.mu.Lock() defer c.mu.Unlock() c.cache[id] = qrs return id } func (c *queryResultSetCache) Get(id uint64) (*queryResultSet, bool) { c.mu.RLock() defer c.mu.RUnlock() qrs, ok := c.cache[id] return qrs, ok } func (c *queryResultSetCache) cleanup(session *Session) { c.mu.Lock() defer c.mu.Unlock() for id, qrs := range c.cache { if qrs.session == session { delete(c.cache, id) } } } golang-github-sap-go-hdb-0.100.10/internal/protocol/result.go000066400000000000000000000131661370256154600237410ustar00rootroot00000000000000/* Copyright 2014 SAP SE Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package protocol import ( "database/sql/driver" "fmt" "github.com/SAP/go-hdb/internal/protocol/encoding" ) 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 uint64 func (id resultsetID) String() string { return fmt.Sprintf("%d", id) } func (id *resultsetID) decode(dec *encoding.Decoder, ph *partHeader) error { *id = resultsetID(dec.Uint64()) return dec.Error() } func (id resultsetID) encode(enc *encoding.Encoder) error { enc.Uint64(uint64(id)); return nil } // TODO cache func newResultFields(size int) []*resultField { return make([]*resultField, size) } // resultField contains database field attributes for result fields. type resultField struct { tableName string schemaName string columnName string columnDisplayName string columnOptions columnOptions tc typeCode fraction int16 length int16 tableNameOffset uint32 schemaNameOffset uint32 columnNameOffset uint32 columnDisplayNameOffset uint32 } // 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.tableName, f.schemaName, f.columnName, f.columnDisplayName, ) } func (f *resultField) Converter() Converter { return f.tc.fieldType() } // TypeName returns the type name of the field. // see https://golang.org/pkg/database/sql/driver/#RowsColumnTypeDatabaseTypeName func (f *resultField) TypeName() string { return f.tc.typeName() } // ScanType returns the scan type of the field. // see https://golang.org/pkg/database/sql/driver/#RowsColumnTypeScanType func (f *resultField) ScanType() DataType { return f.tc.dataType() } // 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.columnDisplayName } func (f *resultField) In() bool { return false } func (f *resultField) Out() bool { return true } func (f *resultField) decode(dec *encoding.Decoder) { f.columnOptions = columnOptions(dec.Int8()) f.tc = typeCode(dec.Int8()) f.fraction = dec.Int16() f.length = dec.Int16() dec.Skip(2) //filler f.tableNameOffset = dec.Uint32() f.schemaNameOffset = dec.Uint32() f.columnNameOffset = dec.Uint32() f.columnDisplayNameOffset = dec.Uint32() } //resultset metadata type resultMetadata struct { resultFields []*resultField } func (r *resultMetadata) String() string { return fmt.Sprintf("result fields %v", r.resultFields) } func (r *resultMetadata) decode(dec *encoding.Decoder, ph *partHeader) error { r.resultFields = newResultFields(ph.numArg()) names := fieldNames{} for i := 0; i < len(r.resultFields); i++ { f := new(resultField) f.decode(dec) r.resultFields[i] = f names.insert(f.tableNameOffset) names.insert(f.schemaNameOffset) names.insert(f.columnNameOffset) names.insert(f.columnDisplayNameOffset) } names.decode(dec) for _, f := range r.resultFields { f.tableName = names.name(f.tableNameOffset) f.schemaName = names.name(f.schemaNameOffset) f.columnName = names.name(f.columnNameOffset) f.columnDisplayName = names.name(f.columnDisplayNameOffset) } //r.resultFieldSet.decode(dec) return dec.Error() } //resultset type resultset struct { resultFields []*resultField fieldValues []driver.Value } func (r *resultset) String() string { return fmt.Sprintf("result fields %v field values %v", r.resultFields, r.fieldValues) } func (r *resultset) decode(dec *encoding.Decoder, ph *partHeader) error { numArg := ph.numArg() cols := len(r.resultFields) r.fieldValues = newFieldValues(numArg * cols) for i := 0; i < numArg; i++ { for j, field := range r.resultFields { var err error if r.fieldValues[i*cols+j], err = decodeRes(dec, field.tc); err != nil { return err } } } return dec.Error() } golang-github-sap-go-hdb-0.100.10/internal/protocol/rowsaffected.go000066400000000000000000000025121370256154600250700ustar00rootroot00000000000000/* 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/protocol/encoding" ) //rows affected const ( raSuccessNoInfo = -2 raExecutionFailed = -3 ) //rows affected type rowsAffected []int32 func (r rowsAffected) String() string { return fmt.Sprintf("%v", []int32(r)) } func (r *rowsAffected) reset(numArg int) { if r == nil || numArg > cap(*r) { *r = make(rowsAffected, numArg) } else { *r = (*r)[:numArg] } } func (r *rowsAffected) decode(dec *encoding.Decoder, ph *partHeader) error { r.reset(ph.numArg()) for i := 0; i < ph.numArg(); i++ { (*r)[i] = dec.Int32() } return dec.Error() } func (r rowsAffected) total() int64 { if r == nil { return 0 } total := int64(0) for _, rows := range r { if rows > 0 { total += int64(rows) } } return total } golang-github-sap-go-hdb-0.100.10/internal/protocol/scanner/000077500000000000000000000000001370256154600235165ustar00rootroot00000000000000golang-github-sap-go-hdb-0.100.10/internal/protocol/scanner/scanner.go000066400000000000000000000136061370256154600255040ustar00rootroot00000000000000// +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" "strings" "unicode" "unicode/utf8" ) // ErrToken is raised when a token is malformed (e.g. string with missing ending quote). var ErrToken = errors.New("invalid token") // Token is the set of lexicals tokens of a SQL query. type Token int // Token constants. const ( EOS Token = iota Error Undefined Operator Delimiter IdentifierDelimiter Identifier QuotedIdentifier Variable PosVariable NamedVariable String Number ) var tokenString = map[Token]string{ EOS: "EndOfString", Error: "Error", Undefined: "Undefined", Operator: "Operator", Delimiter: "Delimiter", IdentifierDelimiter: "IdentifierDelimiter", Identifier: "Identifier", QuotedIdentifier: "QuotedIdentifier", Variable: "Variable", PosVariable: "PosVariable", NamedVariable: "NamedVariable", String: "String", Number: "Number", } func (t Token) String() string { if s, ok := tokenString[t]; ok { return s } return fmt.Sprintf("%s", string(t)) } var compositeOperators = map[string]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 == ':' } // A Scanner implements reading of SQL query tokens. type Scanner struct { s string i int // reading position prev int // previous reading position } // Reset initializes a Scanner with a new SQL statement. func (sc *Scanner) Reset(s string) { sc.s = s sc.i = 0 sc.prev = -1 } func (sc *Scanner) readRune() (rune, bool) { sc.prev = sc.i if sc.i >= len(sc.s) { return 0, false } if c := sc.s[sc.i]; c < utf8.RuneSelf { sc.i++ return rune(c), true } ch, size := utf8.DecodeRuneInString(sc.s[sc.i:]) sc.i += size return ch, true } func (sc *Scanner) unreadRune() { if sc.prev == -1 { panic("unreadRune before readRune") } sc.i = sc.prev sc.prev = -1 } func (sc *Scanner) scanWhitespace() { for { ch, ok := sc.readRune() if !ok { return } if !unicode.IsSpace(ch) { sc.unreadRune() return } } } func (sc *Scanner) scanOperator(ch rune) { ch2, ok := sc.readRune() if !ok { return } if isCompositeOperator(string([]rune{ch, ch2})) { return } sc.unreadRune() } func (sc *Scanner) scanNumeric() { for { ch, ok := sc.readRune() if !ok { return } if !isDigit(ch) { sc.unreadRune() return } } } func (sc *Scanner) scanAlpha() { for { ch, ok := sc.readRune() if !ok { return } if !isAlpha(ch) { sc.unreadRune() return } } } func (sc *Scanner) scanQuotedIdentifier(quote rune) Token { for { ch, ok := sc.readRune() if !ok { return Error } if ch == quote { ch, ok := sc.readRune() if !ok { return QuotedIdentifier } if ch != quote { sc.unreadRune() return QuotedIdentifier } } } } func (sc *Scanner) scanVariable() Token { ch, ok := sc.readRune() if !ok { return Error } if isDigit(ch) { sc.scanNumeric() return PosVariable } sc.scanAlpha() return NamedVariable } func (sc *Scanner) scanNumber() Token { sc.scanNumeric() ch, ok := sc.readRune() if !ok { return Number } if isDecimalSeparator(ch) { sc.scanNumeric() } ch, ok = sc.readRune() if !ok { return Number } if isExp(ch) { if isNumber(ch) { sc.scanNumeric() return Number } return Error } return Number } // Next reads and returns the next token. func (sc *Scanner) Next() (Token, int, int) { sc.scanWhitespace() start := sc.i ch, ok := sc.readRune() if !ok { return EOS, start, sc.i } switch { default: return Error, start, sc.i case isDelimiter(ch): return Delimiter, start, sc.i case isNameDelimiter(ch): return IdentifierDelimiter, start, sc.i case isQuestionMark(ch): return Variable, start, sc.i case isOperator(ch): sc.scanOperator(ch) return Operator, start, sc.i case isIdentifier(ch): sc.scanAlpha() return Identifier, start, sc.i case isSingleQuote(ch) || isDoubleQuote(ch): token := sc.scanQuotedIdentifier(ch) return token, start, sc.i case isColon(ch): token := sc.scanVariable() return token, start, sc.i case isNumber(ch): sc.scanNumber() return Number, start, sc.i } } golang-github-sap-go-hdb-0.100.10/internal/protocol/scanner/scanner_test.go000066400000000000000000000101301370256154600265300ustar00rootroot00000000000000// +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 ( "reflect" "testing" ) type tokenValue struct { token Token value string } var testData = []struct { s string r []tokenValue }{ {``, []tokenValue{}}, // empty {` `, []tokenValue{}}, // only whitespaces { `delete from Invoice where TimeCreated < :end and TimeCreated >= :start;`, []tokenValue{ {Identifier, "delete"}, {Identifier, "from"}, {Identifier, "Invoice"}, {Identifier, "where"}, {Identifier, "TimeCreated"}, {Operator, "<"}, {NamedVariable, ":end"}, {Identifier, "and"}, {Identifier, "TimeCreated"}, {Operator, ">="}, {NamedVariable, ":start"}, {Delimiter, ";"}, }, }, { `delete from _Invoice where _TimeCreated < :end and _TimeCreated >= :start;`, []tokenValue{ {Identifier, "delete"}, {Identifier, "from"}, {Identifier, "_Invoice"}, {Identifier, "where"}, {Identifier, "_TimeCreated"}, {Operator, "<"}, {NamedVariable, ":end"}, {Identifier, "and"}, {Identifier, "_TimeCreated"}, {Operator, ">="}, {NamedVariable, ":start"}, {Delimiter, ";"}, }, }, { `delete from "Invoice" where "TimeCreated" < :end and "TimeCreated" >= :start;`, []tokenValue{ {Identifier, "delete"}, {Identifier, "from"}, {QuotedIdentifier, `"Invoice"`}, {Identifier, "where"}, {QuotedIdentifier, `"TimeCreated"`}, {Operator, "<"}, {NamedVariable, ":end"}, {Identifier, "and"}, {QuotedIdentifier, `"TimeCreated"`}, {Operator, ">="}, {NamedVariable, ":start"}, {Delimiter, ";"}, }, }, { `delete from schema."Invoice";`, []tokenValue{ {Identifier, "delete"}, {Identifier, "from"}, {Identifier, "schema"}, {IdentifierDelimiter, "."}, {QuotedIdentifier, `"Invoice"`}, {Delimiter, ";"}, }, }, { `delete from "schema"."Invoice";`, []tokenValue{ {Identifier, "delete"}, {Identifier, "from"}, {QuotedIdentifier, `"schema"`}, {IdentifierDelimiter, "."}, {QuotedIdentifier, `"Invoice"`}, {Delimiter, ";"}, }, }, { `delete from "Inv"""oice" where "Time""Created" < :end and "Time""Created" >= :start;`, []tokenValue{ {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;`}, }, }, { // call table result query `rsid 1234567890`, []tokenValue{ {Identifier, "rsid"}, {Number, "1234567890"}, }, }, } func testScannerX(t *testing.T) { tvs := make([]tokenValue, 0) scanner := Scanner{} for i, d := range testData { tvs = tvs[:0] scanner.Reset(d.s) for { token, start, end := scanner.Next() if token == EOS { break } tvs = append(tvs, tokenValue{token: token, value: d.s[start:end]}) } if !reflect.DeepEqual(tvs, d.r) { if len(tvs) != len(d.r) { t.Fatalf("different length test %d: %d %d %v %v", i, len(tvs), len(d.r), tvs, d.r) } for j, tv := range tvs { if tv.token != d.r[j].token { t.Fatalf("different token %d %d %s %s", i, j, tv.token, d.r[j].token) } if tv.value != d.r[j].value { t.Fatalf("different value %d %d %s %s", i, j, tv.value, d.r[j].value) } } } } } func TestScanner(t *testing.T) { tests := []struct { name string fct func(t *testing.T) }{ {"scannerX", testScannerX}, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { test.fct(t) }) } } golang-github-sap-go-hdb-0.100.10/internal/protocol/segment.go000066400000000000000000000073751370256154600240720ustar00rootroot00000000000000/* 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/protocol/encoding" ) const ( segmentHeaderSize = 24 ) type commandOptions int8 const ( coNil commandOptions = 0x00 //nolint:deadcode 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( "segmentLength %d segmentOfs %d noOfParts %d, segmentNo %d segmentKind %s", h.segmentLength, h.segmentOfs, h.noOfParts, h.segmentNo, h.segmentKind, ) case skRequest: return fmt.Sprintf( "segmentLength %d segmentOfs %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( "segmentLength %d segmentOfs %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) encode(enc *encoding.Encoder) error { enc.Int32(h.segmentLength) enc.Int32(h.segmentOfs) enc.Int16(h.noOfParts) enc.Int16(h.segmentNo) enc.Int8(int8(h.segmentKind)) switch h.segmentKind { default: //error enc.Zeroes(11) //segmentHeaderLength case skRequest: enc.Int8(int8(h.messageType)) enc.Bool(h.commit) enc.Int8(int8(h.commandOptions)) enc.Zeroes(8) //segmentHeaderSize case skReply: enc.Zeroes(1) //reserved enc.Int16(int16(h.functionCode)) enc.Zeroes(8) //segmentHeaderSize } return nil } // reply || error func (h *segmentHeader) decode(dec *encoding.Decoder) error { h.segmentLength = dec.Int32() h.segmentOfs = dec.Int32() h.noOfParts = dec.Int16() h.segmentNo = dec.Int16() h.segmentKind = segmentKind(dec.Int8()) switch h.segmentKind { default: //error dec.Skip(11) //segmentHeaderLength case skRequest: h.messageType = messageType(dec.Int8()) h.commit = dec.Bool() h.commandOptions = commandOptions(dec.Int8()) dec.Skip(8) //segmentHeaderLength case skReply: dec.Skip(1) //reserved h.functionCode = functionCode(dec.Int16()) dec.Skip(8) //segmentHeaderLength } return dec.Error() } golang-github-sap-go-hdb-0.100.10/internal/protocol/segmentkind.go000066400000000000000000000013741370256154600247310ustar00rootroot00000000000000/* 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 //nolint const ( skInvalid segmentKind = 0 skRequest segmentKind = 1 skReply segmentKind = 2 skError segmentKind = 5 ) golang-github-sap-go-hdb-0.100.10/internal/protocol/segmentkind_string.go000066400000000000000000000014231370256154600263120ustar00rootroot00000000000000// Code generated by "stringer -type=segmentKind"; DO NOT EDIT. package protocol import "strconv" func _() { // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. var x [1]struct{} _ = x[skInvalid-0] _ = x[skRequest-1] _ = x[skReply-2] _ = x[skError-5] } 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.100.10/internal/protocol/session.go000066400000000000000000000606301370256154600241040ustar00rootroot00000000000000/* 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 ( "bufio" "context" "crypto/tls" "database/sql/driver" "fmt" "io" "net" "sync" "time" "golang.org/x/text/transform" "github.com/SAP/go-hdb/driver/dial" "github.com/SAP/go-hdb/internal/unicode" "github.com/SAP/go-hdb/internal/unicode/cesu8" ) //padding const padding = 8 func padBytes(size int) int { if r := size % padding; r != 0 { return padding - r } return 0 } // sesion handling const ( sesRecording = "rec" sesReplay = "rpl" ) type sessionStatus interface { isBad() bool } type sessionConn interface { io.ReadWriteCloser sessionStatus } func newSessionConn(ctx context.Context, address string, dialer dial.Dialer, timeout, tcpKeepAlive time.Duration, tlsConfig *tls.Config) (sessionConn, error) { // session recording if wr, ok := ctx.Value(sesRecording).(io.Writer); ok { conn, err := newDbConn(ctx, address, dialer, timeout, tcpKeepAlive, tlsConfig) if err != nil { return nil, err } return proxyConn{ Reader: io.TeeReader(conn, wr), // teereader: write database replies to writer Writer: conn, Closer: conn, sessionStatus: conn, }, nil } // session replay if rd, ok := ctx.Value(sesReplay).(io.Reader); ok { nwc := nullWriterCloser{} return proxyConn{ Reader: rd, Writer: nwc, Closer: nwc, sessionStatus: nwc, }, nil } return newDbConn(ctx, address, dialer, timeout, tcpKeepAlive, tlsConfig) } type nullWriterCloser struct{} func (n nullWriterCloser) Write(p []byte) (int, error) { return len(p), nil } func (n nullWriterCloser) Close() error { return nil } func (n nullWriterCloser) isBad() bool { return false } // proxy connection type proxyConn struct { io.Reader io.Writer io.Closer sessionStatus } // dbConn wraps the database tcp connection. It sets timeouts and handles driver ErrBadConn behavior. type dbConn struct { address string timeout time.Duration conn net.Conn lastError error // error bad connection } func newDbConn(ctx context.Context, address string, dialer dial.Dialer, timeout, tcpKeepAlive time.Duration, tlsConfig *tls.Config) (*dbConn, error) { conn, err := dialer.DialContext(ctx, address, dial.DialerOptions{Timeout: timeout, TCPKeepAlive: tcpKeepAlive}) if err != nil { return nil, err } // is TLS connection requested? if tlsConfig != nil { conn = tls.Client(conn, tlsConfig) } return &dbConn{address: address, timeout: timeout, conn: conn}, nil } func (c *dbConn) isBad() bool { return c.lastError != nil } func (c *dbConn) deadline() (deadline time.Time) { if c.timeout == 0 { return } return time.Now().Add(c.timeout) } func (c *dbConn) Close() error { return c.conn.Close() } // Read implements the io.Reader interface. func (c *dbConn) Read(b []byte) (n int, err error) { //set timeout if err = c.conn.SetReadDeadline(c.deadline()); err != nil { goto retError } if n, err = c.conn.Read(b); err != nil { goto retError } return retError: plog.Printf("Connection read error local address %s remote address %s: %s", c.conn.LocalAddr(), c.conn.RemoteAddr(), err) c.lastError = err return n, driver.ErrBadConn } // Write implements the io.Writer interface. func (c *dbConn) Write(b []byte) (n int, err error) { //set timeout if err = c.conn.SetWriteDeadline(c.deadline()); err != nil { goto retError } if n, err = c.conn.Write(b); err != nil { goto retError } return retError: plog.Printf("Connection write error local address %s remote address %s: %s", c.conn.LocalAddr(), c.conn.RemoteAddr(), err) c.lastError = err return n, driver.ErrBadConn } // SessionConfig represents the session relevant driver connector options. type SessionConfig interface { Host() string Username() string Password() string Locale() string BufferSize() int FetchSize() int BulkSize() int LobChunkSize() int32 Dialer() dial.Dialer TimeoutDuration() time.Duration TCPKeepAlive() time.Duration Dfv() int TLSConfig() *tls.Config Legacy() bool } const dfvLevel1 = 1 const defaultSessionID = -1 // Session represents a HDB session. type Session struct { cfg SessionConfig sessionID int64 conn sessionConn rd *bufio.Reader wr *bufio.Writer pr *protocolReader pw *protocolWriter //serialize write request - read reply //supports calling session methods in go routines (driver methods with context cancellation) mu sync.Mutex isLocked bool inTx bool // in transaction /* As long as a session is in query mode no other sql statement must be executed. Example: - pinger is active - select with blob fields is executed - scan is hitting the database again (blob streaming) - if in between a ping gets executed (ping selects db) hdb raises error "SQL Error 1033 - error while parsing protocol: invalid lob locator id (piecewise lob reading)" */ inQuery bool // in query } // NewSession creates a new database session. func NewSession(ctx context.Context, cfg SessionConfig) (*Session, error) { var conn sessionConn conn, err := newSessionConn(ctx, cfg.Host(), cfg.Dialer(), cfg.TimeoutDuration(), cfg.TCPKeepAlive(), cfg.TLSConfig()) if err != nil { return nil, err } var bufRd *bufio.Reader var bufWr *bufio.Writer bufferSize := cfg.BufferSize() if bufferSize > 0 { bufRd = bufio.NewReaderSize(conn, bufferSize) bufWr = bufio.NewWriterSize(conn, bufferSize) } else { bufRd = bufio.NewReader(conn) bufWr = bufio.NewWriter(conn) } pw := newProtocolWriter(bufWr) // write upstream if err := pw.writeProlog(); err != nil { return nil, err } pr := newProtocolReader(false, bufRd) // read downstream if err := pr.readProlog(); err != nil { return nil, err } s := &Session{ cfg: cfg, sessionID: defaultSessionID, conn: conn, rd: bufRd, wr: bufWr, pr: pr, pw: pw, } return s, s.authenticate() } // Lock session. func (s *Session) Lock() { s.mu.Lock(); s.isLocked = true } // Unlock session. func (s *Session) Unlock() { s.isLocked = false; s.mu.Unlock() } // checkLock checks if the session is locked func (s *Session) checkLock() { if !s.isLocked { panic("Session is not locked") } } // Kill session. func (s *Session) Kill() { s.checkLock() plog.Printf("Kill session %d", s.sessionID) s.conn.Close() } // Reset resets the session. func (s *Session) Reset() { s.checkLock(); s.SetInQuery(false); QrsCache.cleanup(s) } // Close closes the session. func (s *Session) Close() error { s.checkLock(); QrsCache.cleanup(s); return s.conn.Close() } // InTx indicates, that the session is in transaction mode. func (s *Session) InTx() bool { s.checkLock(); return s.inTx } // SetInTx sets the session transaction mode. func (s *Session) SetInTx(v bool) { s.checkLock(); s.inTx = v } // InQuery indicates, that the session is in query mode. func (s *Session) InQuery() bool { s.checkLock(); return s.inQuery } // SetInQuery sets the session query mode. func (s *Session) SetInQuery(v bool) { s.checkLock(); s.inQuery = v } // IsBad indicates, that the session is in bad state. func (s *Session) IsBad() bool { s.checkLock(); return s.conn.isBad() } // MaxBulkNum returns the maximal number of bulk calls before auto flush. func (s *Session) MaxBulkNum() int { maxBulkNum := s.cfg.BulkSize() if maxBulkNum > maxPartNum { return maxPartNum // max number of parameters (see parameter header) } return maxBulkNum } func (s *Session) authenticate() error { authStepper := newAuth(s.cfg.Username(), s.cfg.Password()) if err := s.authenticateMethod(authStepper); err != nil { return err } if s.sessionID <= 0 { return fmt.Errorf("invalid session id %d", s.sessionID) } return nil } func (s *Session) connectOptions() connectOptions { co := connectOptions{} co.set(coDistributionProtocolVersion, optBooleanType(false)) co.set(coSelectForUpdateSupported, optBooleanType(false)) co.set(coSplitBatchCommands, optBooleanType(true)) co.set(coDataFormatVersion2, optIntType(s.cfg.Dfv())) co.set(coCompleteArrayExecution, optBooleanType(true)) if s.cfg.Locale() != "" { co.set(coClientLocale, optStringType(s.cfg.Locale())) } co.set(coClientDistributionMode, cdmOff) // co.set(coImplicitLobStreaming, optBooleanType(true)) return co } func (s *Session) authenticateMethod(stepper authStepper) error { var auth partReadWriter var err error if auth, err = stepper.next(); err != nil { return err } if err := s.pw.write(s.sessionID, mtAuthenticate, false, auth); err != nil { return err } if auth, err = stepper.next(); err != nil { return err } if err := s.pr.iterateParts(func(ph *partHeader) { if ph.partKind == pkAuthentication { s.pr.read(auth) } }); err != nil { return err } if auth, err = stepper.next(); err != nil { return err } id := newClientID() co := s.connectOptions() if err := s.pw.write(s.sessionID, mtConnect, false, auth, id, co); err != nil { return err } if auth, err = stepper.next(); err != nil { return err } if err := s.pr.iterateParts(func(ph *partHeader) { switch ph.partKind { case pkAuthentication: s.pr.read(auth) case pkConnectOptions: s.pr.read(&co) // set data format version // TODO generalize for sniffer s.pr.setDfv(int(co[coDataFormatVersion2].(optIntType))) } }); err != nil { return err } s.sessionID = s.pr.sessionID() return nil } // QueryDirect executes a query without query parameters. func (s *Session) QueryDirect(query string) (driver.Rows, error) { s.checkLock() s.SetInQuery(true) // allow e.g inserts as query -> handle commit like in ExecDirect if err := s.pw.write(s.sessionID, mtExecuteDirect, !s.inTx, command(query)); err != nil { return nil, err } qr := &queryResult{} meta := &resultMetadata{} resSet := &resultset{} if err := s.pr.iterateParts(func(ph *partHeader) { switch ph.partKind { case pkResultMetadata: s.pr.read(meta) qr.fields = meta.resultFields case pkResultsetID: s.pr.read((*resultsetID)(&qr._rsID)) case pkResultset: resSet.resultFields = qr.fields s.pr.read(resSet) qr.fieldValues = resSet.fieldValues qr.attributes = ph.partAttributes } }); err != nil { return nil, err } if qr._rsID == 0 { // non select query return noResult, nil } return newQueryResultSet(s, qr), nil } // ExecDirect executes a sql statement without statement parameters. func (s *Session) ExecDirect(query string) (driver.Result, error) { s.checkLock() if err := s.pw.write(s.sessionID, mtExecuteDirect, !s.inTx, command(query)); err != nil { return nil, err } rows := &rowsAffected{} var numRow int64 if err := s.pr.iterateParts(func(ph *partHeader) { if ph.partKind == pkRowsAffected { s.pr.read(rows) numRow = rows.total() } }); err != nil { return nil, err } if s.pr.functionCode() == fcDDL { return driver.ResultNoRows, nil } return driver.RowsAffected(numRow), nil } // Prepare prepares a sql statement. func (s *Session) Prepare(query string) (*PrepareResult, error) { s.checkLock() if err := s.pw.write(s.sessionID, mtPrepare, false, command(query)); err != nil { return nil, err } pr := &PrepareResult{} resMeta := &resultMetadata{} prmMeta := ¶meterMetadata{} if err := s.pr.iterateParts(func(ph *partHeader) { switch ph.partKind { case pkStatementID: s.pr.read((*statementID)(&pr.stmtID)) case pkResultMetadata: s.pr.read(resMeta) pr.resultFields = resMeta.resultFields case pkParameterMetadata: s.pr.read(prmMeta) pr.prmFields = prmMeta.parameterFields } }); err != nil { return nil, err } pr.fc = s.pr.functionCode() return pr, nil } // Exec executes a sql statement. func (s *Session) Exec(pr *PrepareResult, args []driver.NamedValue) (driver.Result, error) { s.checkLock() if err := s.pw.write(s.sessionID, mtExecute, !s.inTx, statementID(pr.stmtID), newInputParameters(pr.prmFields, args)); err != nil { return nil, err } rows := &rowsAffected{} var ids []locatorID lobReply := &writeLobReply{} var numRow int64 if err := s.pr.iterateParts(func(ph *partHeader) { switch ph.partKind { case pkRowsAffected: s.pr.read(rows) numRow = rows.total() case pkWriteLobReply: s.pr.read(lobReply) ids = lobReply.ids } }); err != nil { return nil, err } fc := s.pr.functionCode() if len(ids) != 0 { /* writeLobParameters: - chunkReaders - nil (no callResult, exec does not have output parameters) */ if err := s.encodeLobs(nil, ids, pr.prmFields, args); err != nil { return nil, err } } if fc == fcDDL { return driver.ResultNoRows, nil } return driver.RowsAffected(numRow), nil } // QueryCall executes a stored procecure (by Query). func (s *Session) QueryCall(pr *PrepareResult, args []driver.NamedValue) (driver.Rows, error) { s.checkLock() s.SetInQuery(true) /* only in args invariant: #inPrmFields == #args */ var inPrmFields, outPrmFields []*parameterField for _, f := range pr.prmFields { if f.In() { inPrmFields = append(inPrmFields, f) } if f.Out() { outPrmFields = append(outPrmFields, f) } } if err := s.pw.write(s.sessionID, mtExecute, false, statementID(pr.stmtID), newInputParameters(inPrmFields, args)); err != nil { return nil, err } /* call without lob input parameters: --> callResult output parameter values are set after read call call with lob input parameters: --> callResult output parameter values are set after last lob input write */ cr, ids, err := s.readCall(outPrmFields) if err != nil { return nil, err } if len(ids) != 0 { /* writeLobParameters: - chunkReaders - cr (callResult output parameters are set after all lob input parameters are written) */ if err := s.encodeLobs(cr, ids, inPrmFields, args); err != nil { return nil, err } } // legacy mode? if s.cfg.Legacy() { cr.appendTableRefFields() // TODO review for _, qr := range cr.qrs { // add to cache QrsCache.set(qr._rsID, newQueryResultSet(s, qr)) } } else { cr.appendTableRowsFields(s) } return newQueryResultSet(s, cr), nil } // ExecCall executes a stored procecure (by Exec). func (s *Session) ExecCall(pr *PrepareResult, args []driver.NamedValue) (driver.Result, error) { s.checkLock() /* in,- and output args invariant: #prmFields == #args */ var inPrmFields, outPrmFields []*parameterField var inArgs, outArgs []driver.NamedValue for i, f := range pr.prmFields { if f.In() { inPrmFields = append(inPrmFields, f) inArgs = append(inArgs, args[i]) } if f.Out() { outPrmFields = append(outPrmFields, f) outArgs = append(outArgs, args[i]) } } if err := s.pw.write(s.sessionID, mtExecute, false, statementID(pr.stmtID), newInputParameters(inPrmFields, inArgs)); err != nil { return nil, err } /* call without lob input parameters: --> callResult output parameter values are set after read call call with lob input parameters: --> callResult output parameter values are set after last lob input write */ cr, ids, err := s.readCall(outPrmFields) if err != nil { return nil, err } if len(ids) != 0 { /* writeLobParameters: - chunkReaders - cr (callResult output parameters are set after all lob input parameters are written) */ if err := s.encodeLobs(cr, ids, inPrmFields, inArgs); err != nil { return nil, err } } // TODO release v1.0.0 - assign output parameters return nil, fmt.Errorf("not implemented yet") //return driver.ResultNoRows, nil } func (s *Session) readCall(outputFields []*parameterField) (*callResult, []locatorID, error) { cr := &callResult{outputFields: outputFields} //var qrs []*QueryResult var qr *queryResult var ids []locatorID outPrms := &outputParameters{} meta := &resultMetadata{} resSet := &resultset{} lobReply := &writeLobReply{} if err := s.pr.iterateParts(func(ph *partHeader) { switch ph.partKind { case pkOutputParameters: outPrms.outputFields = cr.outputFields s.pr.read(outPrms) cr.fieldValues = outPrms.fieldValues case pkResultMetadata: /* procedure call with table parameters does return metadata for each table sequence: metadata, resultsetID, resultset but: - resultset might not be provided for all tables - so, 'additional' query result is detected by new metadata part */ qr = &queryResult{} cr.qrs = append(cr.qrs, qr) s.pr.read(meta) qr.fields = meta.resultFields case pkResultset: resSet.resultFields = qr.fields s.pr.read(resSet) qr.fieldValues = resSet.fieldValues qr.attributes = ph.partAttributes case pkResultsetID: s.pr.read((*resultsetID)(&qr._rsID)) case pkWriteLobReply: s.pr.read(lobReply) ids = lobReply.ids } }); err != nil { return nil, nil, err } // init fieldValues if cr.fieldValues == nil { cr.fieldValues = newFieldValues(0) } for _, qr := range cr.qrs { if qr.fieldValues == nil { qr.fieldValues = newFieldValues(0) } } return cr, ids, nil } // Query executes a query. func (s *Session) Query(pr *PrepareResult, args []driver.NamedValue) (driver.Rows, error) { s.checkLock() s.SetInQuery(true) // allow e.g inserts as query -> handle commit like in exec if err := s.pw.write(s.sessionID, mtExecute, !s.inTx, statementID(pr.stmtID), newInputParameters(pr.prmFields, args)); err != nil { return nil, err } qr := &queryResult{fields: pr.resultFields} resSet := &resultset{} if err := s.pr.iterateParts(func(ph *partHeader) { switch ph.partKind { case pkResultsetID: s.pr.read((*resultsetID)(&qr._rsID)) case pkResultset: resSet.resultFields = qr.fields s.pr.read(resSet) qr.fieldValues = resSet.fieldValues qr.attributes = ph.partAttributes } }); err != nil { return nil, err } if qr._rsID == 0 { // non select query return noResult, nil } return newQueryResultSet(s, qr), nil } // FetchNext fetches next chunk in query result set. func (s *Session) fetchNext(rr rowsResult) error { s.checkLock() qr, err := rr.queryResult() if err != nil { return err } if err := s.pw.write(s.sessionID, mtFetchNext, false, resultsetID(qr._rsID), fetchsize(s.cfg.FetchSize())); err != nil { return err } resSet := &resultset{} return s.pr.iterateParts(func(ph *partHeader) { if ph.partKind == pkResultset { resSet.resultFields = qr.fields s.pr.read(resSet) qr.fieldValues = resSet.fieldValues qr.attributes = ph.partAttributes } }) } // DropStatementID releases the hdb statement handle. func (s *Session) DropStatementID(id uint64) error { s.checkLock() s.SetInQuery(false) if err := s.pw.write(s.sessionID, mtDropStatementID, false, statementID(id)); err != nil { return err } return s.pr.readSkip() } // CloseResultsetID releases the hdb resultset handle. func (s *Session) CloseResultsetID(id uint64) error { s.checkLock() s.SetInQuery(false) if err := s.pw.write(s.sessionID, mtCloseResultset, false, resultsetID(id)); err != nil { return err } return s.pr.readSkip() } // Commit executes a database commit. func (s *Session) Commit() error { s.checkLock() s.SetInQuery(false) if err := s.pw.write(s.sessionID, mtCommit, false); err != nil { return err } if err := s.pr.readSkip(); err != nil { return err } s.inTx = false return nil } // Rollback executes a database rollback. func (s *Session) Rollback() error { s.checkLock() s.SetInQuery(false) if err := s.pw.write(s.sessionID, mtRollback, false); err != nil { return err } if err := s.pr.readSkip(); err != nil { return err } s.inTx = false return nil } // decodeLobs decodes (reads from db) output lob or result lob parameters. // read lob reply // - seems like readLobreply returns only a result for one lob - even if more then one is requested // --> read single lobs func (s *Session) decodeLobs(descr *lobOutDescr, wr io.Writer) error { s.Lock() defer s.Unlock() var err error if descr.isCharBased { wrcl := transform.NewWriter(wr, unicode.Cesu8ToUtf8Transformer) // CESU8 transformer err = s._decodeLobs(descr, wrcl, func(b []byte) (int64, error) { // Caution: hdb counts 4 byte utf-8 encodings (cesu-8 6 bytes) as 2 (3 byte) chars numChars := int64(0) for len(b) > 0 { if !cesu8.FullRune(b) { // return 0, fmt.Errorf("lob chunk consists of incomplete CESU-8 runes") } _, size := cesu8.DecodeRune(b) b = b[size:] numChars++ if size == cesu8.CESUMax { numChars++ } } return numChars, nil }) } else { err = s._decodeLobs(descr, wr, func(b []byte) (int64, error) { return int64(len(b)), nil }) } if pw, ok := wr.(*io.PipeWriter); ok { // if the writer is a pipe-end -> close at the end if err != nil { pw.CloseWithError(err) } else { pw.Close() } } return err } func (s *Session) _decodeLobs(descr *lobOutDescr, wr io.Writer, countChars func(b []byte) (int64, error)) error { lobChunkSize := int64(s.cfg.LobChunkSize()) chunkSize := func(numChar, ofs int64) int32 { chunkSize := numChar - ofs if chunkSize > lobChunkSize { return int32(lobChunkSize) } return int32(chunkSize) } if _, err := wr.Write(descr.b); err != nil { return err } lobRequest := &readLobRequest{} lobRequest.id = descr.id lobReply := &readLobReply{} eof := descr.opt.isLastData() ofs, err := countChars(descr.b) if err != nil { return err } for !eof { lobRequest.ofs += ofs lobRequest.chunkSize = chunkSize(descr.numChar, ofs) if err := s.pw.write(s.sessionID, mtWriteLob, false, lobRequest); err != nil { return err } if err := s.pr.iterateParts(func(ph *partHeader) { if ph.partKind == pkReadLobReply { s.pr.read(lobReply) } }); err != nil { return err } if lobReply.id != lobRequest.id { return fmt.Errorf("internal error: invalid lob locator %d - expected %d", lobReply.id, lobRequest.id) } if _, err := wr.Write(lobReply.b); err != nil { return err } ofs, err = countChars(lobReply.b) if err != nil { return err } eof = lobReply.opt.isLastData() } return nil } // encodeLobs encodes (write to db) input lob parameters. func (s *Session) encodeLobs(cr *callResult, ids []locatorID, inPrmFields []*parameterField, args []driver.NamedValue) error { chunkSize := int(s.cfg.LobChunkSize()) readers := make([]io.Reader, 0, len(ids)) descrs := make([]*writeLobDescr, 0, len(ids)) j := 0 for i, f := range inPrmFields { if f.tc.isLob() { rd, ok := args[i].Value.(io.Reader) if !ok { return fmt.Errorf("protocol error: invalid lob parameter %[1]T %[1]v - io.Reader expected", args[i].Value) } if f.tc.isCharBased() { rd = transform.NewReader(rd, unicode.Utf8ToCesu8Transformer) // CESU8 transformer } if j >= len(ids) { return fmt.Errorf("protocol error: invalid number of lob parameter ids %d", len(ids)) } readers = append(readers, rd) descrs = append(descrs, &writeLobDescr{id: ids[j]}) j++ } } writeLobRequest := &writeLobRequest{} for len(descrs) != 0 { if len(descrs) != len(ids) { return fmt.Errorf("protocol error: invalid number of lob parameter ids %d - expected %d", len(descrs), len(ids)) } for i, descr := range descrs { // check if ids and descrs are in sync if descr.id != ids[i] { return fmt.Errorf("protocol error: lob parameter id mismatch %d - expected %d", descr.id, ids[i]) } } // TODO check total size limit for i, descr := range descrs { descr.b = make([]byte, chunkSize) size, err := readers[i].Read(descr.b) descr.b = descr.b[:size] if err != nil && err != io.EOF { return err } descr.ofs = -1 //offset (-1 := append) descr.opt = loDataincluded if err == io.EOF { descr.opt |= loLastdata } } writeLobRequest.descrs = descrs if err := s.pw.write(s.sessionID, mtReadLob, false, writeLobRequest); err != nil { return err } lobReply := &writeLobReply{} outPrms := &outputParameters{} if err := s.pr.iterateParts(func(ph *partHeader) { switch ph.partKind { case pkOutputParameters: outPrms.outputFields = cr.outputFields s.pr.read(outPrms) cr.fieldValues = outPrms.fieldValues case pkWriteLobReply: s.pr.read(lobReply) ids = lobReply.ids } }); err != nil { return err } // remove done descr and readers j := 0 for i, descr := range descrs { if !descr.opt.isLastData() { descrs[j] = descr readers[j] = readers[i] j++ } } descrs = descrs[:j] readers = readers[:j] } return nil } golang-github-sap-go-hdb-0.100.10/internal/protocol/sniffer.go000066400000000000000000000130701370256154600240510ustar00rootroot00000000000000/* Copyright 2020 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 // TODO Sniffer /* sniffer: - complete for go-hdb: especially call with table parameters - delete caches for statement and result - don't ignore part read error - example: read scramsha256InitialReply got silently stuck because methodname check failed - test with python client and handle surprises - analyze for not ignoring part read errors */ import ( "bufio" "io" "net" "sync" ) // A Sniffer is a simple proxy for logging hdb protocol requests and responses. type Sniffer struct { conn net.Conn dbConn net.Conn //client clRd *bufio.Reader clWr *bufio.Writer //database dbRd *bufio.Reader dbWr *bufio.Writer // reader upRd *sniffUpReader downRd *sniffDownReader } // 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, dbConn net.Conn) *Sniffer { //TODO - review setting values here trace = true debug = true s := &Sniffer{ conn: conn, dbConn: dbConn, // buffered write to client clWr: bufio.NewWriter(conn), // buffered write to db dbWr: bufio.NewWriter(dbConn), } //read from client connection and write to db buffer s.clRd = bufio.NewReader(io.TeeReader(conn, s.dbWr)) //read from db and write to client connection buffer s.dbRd = bufio.NewReader(io.TeeReader(dbConn, s.clWr)) s.upRd = newSniffUpReader(s.clRd) s.downRd = newSniffDownReader(s.dbRd, s.upRd) return s } // Do starts the protocol request and response logging. func (s *Sniffer) Do() error { defer s.dbConn.Close() defer s.conn.Close() if err := s.upRd.pr.readProlog(); err != nil { return err } if err := s.dbWr.Flush(); err != nil { return err } if err := s.downRd.pr.readProlog(); err != nil { return err } if err := s.clWr.Flush(); err != nil { return err } for { //up stream if err := s.upRd.readMsg(); err != nil { return err // err == io.EOF: connection closed by client } if err := s.dbWr.Flush(); err != nil { return err } //down stream if err := s.downRd.readMsg(); err != nil { if _, ok := err.(*hdbErrors); !ok { //if hdbErrors continue return err } } if err := s.clWr.Flush(); err != nil { return err } } } type sniffReader struct { pr *protocolReader } func newSniffReader(upStream bool, rd *bufio.Reader) *sniffReader { return &sniffReader{pr: newProtocolReader(upStream, rd)} } type sniffUpReader struct{ *sniffReader } func newSniffUpReader(rd *bufio.Reader) *sniffUpReader { return &sniffUpReader{sniffReader: newSniffReader(true, rd)} } type resMetaCache struct { mu sync.RWMutex cache map[uint64]*resultMetadata } func newResMetaCache() *resMetaCache { return &resMetaCache{cache: make(map[uint64]*resultMetadata)} } func (c *resMetaCache) put(stmtID uint64, resMeta *resultMetadata) { c.mu.Lock() defer c.mu.Unlock() c.cache[stmtID] = resMeta } type prmMetaCache struct { mu sync.RWMutex cache map[uint64]*parameterMetadata } func newPrmMetaCache() *prmMetaCache { return &prmMetaCache{cache: make(map[uint64]*parameterMetadata)} } func (c *prmMetaCache) put(stmtID uint64, prmMeta *parameterMetadata) { c.mu.Lock() defer c.mu.Unlock() c.cache[stmtID] = prmMeta } func (c *prmMetaCache) get(stmtID uint64) *parameterMetadata { c.mu.RLock() defer c.mu.RUnlock() return c.cache[stmtID] } var _resMetaCache = newResMetaCache() var _prmMetaCache = newPrmMetaCache() func (r *sniffUpReader) readMsg() error { var stmtID uint64 return r.pr.iterateParts(func(ph *partHeader) { switch ph.partKind { case pkStatementID: r.pr.read((*statementID)(&stmtID)) // case pkResultMetadata: // r.pr.read(resMeta) case pkParameters: prmMeta := _prmMetaCache.get(stmtID) prms := &inputParameters{inputFields: prmMeta.parameterFields} // TODO only input parameters r.pr.read(prms) } }) } type sniffDownReader struct { *sniffReader resMeta *resultMetadata prmMeta *parameterMetadata } func newSniffDownReader(rd *bufio.Reader, upRd *sniffUpReader) *sniffDownReader { return &sniffDownReader{ sniffReader: newSniffReader(false, rd), resMeta: &resultMetadata{}, prmMeta: ¶meterMetadata{}, } } func (r *sniffDownReader) readMsg() error { var stmtID uint64 //resMeta := &resultMetadata{} //prmMeta := ¶meterMetadata{} if err := r.pr.iterateParts(func(ph *partHeader) { switch ph.partKind { case pkStatementID: r.pr.read((*statementID)(&stmtID)) case pkResultMetadata: r.pr.read(r.resMeta) case pkParameterMetadata: r.pr.read(r.prmMeta) case pkOutputParameters: outFields := []*parameterField{} for _, f := range r.prmMeta.parameterFields { if f.Out() { outFields = append(outFields, f) } } outPrms := &outputParameters{outputFields: outFields} r.pr.read(outPrms) case pkResultset: resSet := &resultset{resultFields: r.resMeta.resultFields} r.pr.read(resSet) } }); err != nil { return err } _resMetaCache.put(stmtID, r.resMeta) _prmMetaCache.put(stmtID, r.prmMeta) return nil } golang-github-sap-go-hdb-0.100.10/internal/protocol/statementcontext.go000066400000000000000000000021121370256154600260210ustar00rootroot00000000000000/* 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/protocol/encoding" ) type statementContext plainOptions func (c statementContext) String() string { typedSc := make(map[statementContextType]interface{}) for k, v := range c { typedSc[statementContextType(k)] = v } return fmt.Sprintf("options %s", typedSc) } func (c *statementContext) decode(dec *encoding.Decoder, ph *partHeader) error { *c = statementContext{} // no reuse of maps - create new one plainOptions(*c).decode(dec, ph.numArg()) return dec.Error() } golang-github-sap-go-hdb-0.100.10/internal/protocol/statementcontexttype.go000066400000000000000000000014061370256154600267300ustar00rootroot00000000000000/* 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 //nolint const ( scStatementSequenceInfo statementContextType = 1 scServerExecutionTime statementContextType = 2 ) golang-github-sap-go-hdb-0.100.10/internal/protocol/statementcontexttype_string.go000066400000000000000000000014531370256154600303200ustar00rootroot00000000000000// Code generated by "stringer -type=statementContextType"; DO NOT EDIT. package protocol import "strconv" func _() { // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. var x [1]struct{} _ = x[scStatementSequenceInfo-1] _ = x[scServerExecutionTime-2] } 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.100.10/internal/protocol/statementid.go000066400000000000000000000017111370256154600247350ustar00rootroot00000000000000/* 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/protocol/encoding" ) type statementID uint64 func (id statementID) String() string { return fmt.Sprintf("%d", id) } func (id *statementID) decode(dec *encoding.Decoder, ph *partHeader) error { *id = statementID(dec.Uint64()) return dec.Error() } func (id statementID) encode(enc *encoding.Encoder) error { enc.Uint64(uint64(id)); return nil } golang-github-sap-go-hdb-0.100.10/internal/protocol/topology.go000066400000000000000000000021711370256154600242710ustar00rootroot00000000000000/* 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/protocol/encoding" ) type topologyInformation multiLineOptions func (o topologyInformation) String() string { mlo := make([]map[topologyOption]interface{}, len(o)) for i, po := range o { typedPo := make(map[topologyOption]interface{}) for k, v := range po { typedPo[topologyOption(k)] = v } mlo[i] = typedPo } return fmt.Sprintf("options %s", mlo) } func (o *topologyInformation) decode(dec *encoding.Decoder, ph *partHeader) error { (*multiLineOptions)(o).decode(dec, ph.numArg()) return dec.Error() } golang-github-sap-go-hdb-0.100.10/internal/protocol/topologyoption.go000066400000000000000000000021551370256154600255240ustar00rootroot00000000000000/* 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 //nolint 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.100.10/internal/protocol/topologyoption_string.go000066400000000000000000000021571370256154600271140ustar00rootroot00000000000000// Code generated by "stringer -type=topologyOption"; DO NOT EDIT. package protocol import "strconv" func _() { // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. var x [1]struct{} _ = x[toHostName-1] _ = x[toHostPortnumber-2] _ = x[toTenantName-3] _ = x[toLoadfactor-4] _ = x[toVolumeID-5] _ = x[toIsMaster-6] _ = x[toIsCurrentSession-7] _ = x[toServiceType-8] _ = x[toNetworkDomain-9] _ = x[toIsStandby-10] _ = x[toAllIPAddresses-11] _ = x[toAllHostNames-12] } 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.100.10/internal/protocol/transactionflags.go000066400000000000000000000021061370256154600257550ustar00rootroot00000000000000/* 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/protocol/encoding" ) type transactionFlags plainOptions func (f transactionFlags) String() string { typedSc := make(map[transactionFlagType]interface{}) for k, v := range f { typedSc[transactionFlagType(k)] = v } return fmt.Sprintf("flags %s", typedSc) } func (f *transactionFlags) decode(dec *encoding.Decoder, ph *partHeader) error { *f = transactionFlags{} // no reuse of maps - create new one plainOptions(*f).decode(dec, ph.numArg()) return dec.Error() } golang-github-sap-go-hdb-0.100.10/internal/protocol/transactionflagtype.go000066400000000000000000000021121370256154600264710ustar00rootroot00000000000000/* 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 //nolint 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.100.10/internal/protocol/transactionflagtype_string.go000066400000000000000000000020621370256154600300630ustar00rootroot00000000000000// Code generated by "stringer -type=transactionFlagType"; DO NOT EDIT. package protocol import "strconv" func _() { // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. var x [1]struct{} _ = x[tfRolledback-0] _ = x[tfCommited-1] _ = x[tfNewIsolationLevel-2] _ = x[tfDDLCommitmodeChanged-3] _ = x[tfWriteTransactionStarted-4] _ = x[tfNowriteTransactionStarted-5] _ = x[tfSessionClosingTransactionError-6] } 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.100.10/internal/protocol/typecode.go000066400000000000000000000147021370256154600242340ustar00rootroot00000000000000/* 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" "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 //nolint const ( tcNullL typeCode = 0x00 tcTinyint typeCode = 0x01 tcSmallint typeCode = 0x02 tcInteger typeCode = 0x03 tcBigint typeCode = 0x04 tcDecimal typeCode = 0x05 tcReal typeCode = 0x06 tcDouble typeCode = 0x07 tcChar typeCode = 0x08 tcVarchar typeCode = 0x09 // changed from tcVarchar1 to tcVarchar (ref hdbclient) tcNchar typeCode = 0x0A tcNvarchar typeCode = 0x0B tcBinary typeCode = 0x0C tcVarbinary typeCode = 0x0D tcDate typeCode = 0x0E tcTime typeCode = 0x0F tcTimestamp typeCode = 0x10 tcTimetz typeCode = 0x11 tcTimeltz typeCode = 0x12 tcTimestampTz typeCode = 0x13 tcTimestampLtz typeCode = 0x14 tcIntervalYm typeCode = 0x15 tcIntervalDs typeCode = 0x16 tcRowid typeCode = 0x17 tcUrowid typeCode = 0x18 tcClob typeCode = 0x19 tcNclob typeCode = 0x1A tcBlob typeCode = 0x1B tcBoolean typeCode = 0x1C tcString typeCode = 0x1D tcNstring typeCode = 0x1E tcLocator typeCode = 0x1F tcNlocator typeCode = 0x20 tcBstring typeCode = 0x21 tcDecimalDigitArray typeCode = 0x22 tcVarchar2 typeCode = 0x23 tcTable typeCode = 0x2D tcSmalldecimal typeCode = 0x2f // inserted (not existent in hdbclient) tcAbapstream typeCode = 0x30 tcAbapstruct typeCode = 0x31 tcAarray typeCode = 0x32 tcText typeCode = 0x33 tcShorttext typeCode = 0x34 tcBintext typeCode = 0x35 tcAlphanum typeCode = 0x37 tcLongdate typeCode = 0x3D tcSeconddate typeCode = 0x3E tcDaydate typeCode = 0x3F tcSecondtime typeCode = 0x40 tcClocator typeCode = 0x46 tcBlobDiskReserved typeCode = 0x47 tcClobDiskReserved typeCode = 0x48 tcNclobDiskReserved typeCode = 0x49 tcStGeometry typeCode = 0x4A tcStPoint typeCode = 0x4B tcFixed16 typeCode = 0x4C tcAbapItab typeCode = 0x4D tcRecordRowStore typeCode = 0x4E tcRecordColumnStore typeCode = 0x4F tcFixed8 typeCode = 0x51 tcFixed12 typeCode = 0x52 tcCiphertext typeCode = 0x5A // additional internal typecodes tcTableRef typeCode = 0x7e // 126 tcTableRows typeCode = 0x7f // 127 ) func (tc typeCode) isLob() bool { return tc == tcClob || tc == tcNclob || tc == tcBlob || tc == tcText || tc == tcBintext || tc == tcLocator || tc == tcNlocator } func (tc typeCode) isCharBased() bool { return tc == tcNvarchar || tc == tcNstring || tc == tcNclob || tc == tcText || tc == tcBintext } func (tc typeCode) isVariableLength() bool { return tc == tcChar || tc == tcNchar || tc == tcVarchar || tc == tcNvarchar || tc == tcBinary || tc == tcVarbinary || tc == tcShorttext || tc == tcAlphanum } func (tc typeCode) isIntegerType() bool { return tc == tcTinyint || tc == tcSmallint || tc == tcInteger || tc == tcBigint } func (tc typeCode) isDecimalType() bool { return tc == tcSmalldecimal || tc == tcDecimal } // see hdbclient func (tc typeCode) encTc() typeCode { switch tc { default: return tc case tcText, tcBintext, tcLocator: return tcNclob } } /* tcBintext: - protocol returns tcLocator for tcBintext - see dataTypeMap and encTc */ var dataTypeMap = map[typeCode]DataType{ tcTinyint: DtTinyint, tcSmallint: DtSmallint, tcInteger: DtInteger, tcBigint: DtBigint, tcReal: DtReal, tcDouble: DtDouble, tcDate: DtTime, tcTime: DtTime, tcTimestamp: DtTime, tcLongdate: DtTime, tcSeconddate: DtTime, tcDaydate: DtTime, tcSecondtime: DtTime, tcDecimal: DtDecimal, tcChar: DtString, tcVarchar: DtString, tcString: DtString, tcAlphanum: DtString, tcNchar: DtString, tcNvarchar: DtString, tcNstring: DtString, tcShorttext: DtString, tcBinary: DtBytes, tcVarbinary: DtBytes, tcBlob: DtLob, tcClob: DtLob, tcNclob: DtLob, tcText: DtLob, tcBintext: DtLob, tcTableRef: DtString, tcTableRows: DtRows, } // DataType converts a type code into one of the supported data types by the driver. func (tc typeCode) dataType() DataType { dt, ok := dataTypeMap[tc] if !ok { panic(fmt.Sprintf("Missing DataType for typeCode %s", tc)) } return dt } // typeName returns the database type name. // see https://golang.org/pkg/database/sql/driver/#RowsColumnTypeDatabaseTypeName func (tc typeCode) typeName() string { return strings.ToUpper(tc.String()[2:]) } var tcFieldTypeMap = map[typeCode]fieldType{ tcTinyint: tinyintType, tcSmallint: smallintType, tcInteger: integerType, tcBigint: bigintType, tcReal: realType, tcDouble: doubleType, tcDate: dateType, tcTime: timeType, tcTimestamp: timestampType, tcLongdate: longdateType, tcSeconddate: seconddateType, tcDaydate: daydateType, tcSecondtime: secondtimeType, tcDecimal: decimalType, tcChar: varType, tcVarchar: varType, tcString: varType, tcAlphanum: alphaType, tcNchar: cesu8Type, tcNvarchar: cesu8Type, tcNstring: cesu8Type, tcShorttext: cesu8Type, tcBinary: varType, tcVarbinary: varType, tcBlob: lobVarType, tcClob: lobVarType, tcNclob: lobCESU8Type, tcText: lobCESU8Type, // tcBintext: lobCESU8Type, tcBintext: lobVarType, tcLocator: lobCESU8Type, //tcNlocator: lobCESU8Type, tcNlocator: lobVarType, } func (tc typeCode) fieldType() fieldType { f, ok := tcFieldTypeMap[tc] if !ok { panic(fmt.Sprintf("Missing FieldType for typeCode %s", tc)) } return f } golang-github-sap-go-hdb-0.100.10/internal/protocol/typecode_string.go000066400000000000000000000075061370256154600256260ustar00rootroot00000000000000// Code generated by "stringer -type=typeCode"; DO NOT EDIT. package protocol import "strconv" func _() { // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. var x [1]struct{} _ = x[tcNullL-0] _ = x[tcTinyint-1] _ = x[tcSmallint-2] _ = x[tcInteger-3] _ = x[tcBigint-4] _ = x[tcDecimal-5] _ = x[tcReal-6] _ = x[tcDouble-7] _ = x[tcChar-8] _ = x[tcVarchar-9] _ = x[tcNchar-10] _ = x[tcNvarchar-11] _ = x[tcBinary-12] _ = x[tcVarbinary-13] _ = x[tcDate-14] _ = x[tcTime-15] _ = x[tcTimestamp-16] _ = x[tcTimetz-17] _ = x[tcTimeltz-18] _ = x[tcTimestampTz-19] _ = x[tcTimestampLtz-20] _ = x[tcIntervalYm-21] _ = x[tcIntervalDs-22] _ = x[tcRowid-23] _ = x[tcUrowid-24] _ = x[tcClob-25] _ = x[tcNclob-26] _ = x[tcBlob-27] _ = x[tcBoolean-28] _ = x[tcString-29] _ = x[tcNstring-30] _ = x[tcLocator-31] _ = x[tcNlocator-32] _ = x[tcBstring-33] _ = x[tcDecimalDigitArray-34] _ = x[tcVarchar2-35] _ = x[tcTable-45] _ = x[tcSmalldecimal-47] _ = x[tcAbapstream-48] _ = x[tcAbapstruct-49] _ = x[tcAarray-50] _ = x[tcText-51] _ = x[tcShorttext-52] _ = x[tcBintext-53] _ = x[tcAlphanum-55] _ = x[tcLongdate-61] _ = x[tcSeconddate-62] _ = x[tcDaydate-63] _ = x[tcSecondtime-64] _ = x[tcClocator-70] _ = x[tcBlobDiskReserved-71] _ = x[tcClobDiskReserved-72] _ = x[tcNclobDiskReserved-73] _ = x[tcStGeometry-74] _ = x[tcStPoint-75] _ = x[tcFixed16-76] _ = x[tcAbapItab-77] _ = x[tcRecordRowStore-78] _ = x[tcRecordColumnStore-79] _ = x[tcFixed8-81] _ = x[tcFixed12-82] _ = x[tcCiphertext-90] _ = x[tcTableRef-126] _ = x[tcTableRows-127] } const ( _typeCode_name_0 = "tcNullLtcTinyinttcSmallinttcIntegertcBiginttcDecimaltcRealtcDoubletcChartcVarchartcNchartcNvarchartcBinarytcVarbinarytcDatetcTimetcTimestamptcTimetztcTimeltztcTimestampTztcTimestampLtztcIntervalYmtcIntervalDstcRowidtcUrowidtcClobtcNclobtcBlobtcBooleantcStringtcNstringtcLocatortcNlocatortcBstringtcDecimalDigitArraytcVarchar2" _typeCode_name_1 = "tcTable" _typeCode_name_2 = "tcSmalldecimaltcAbapstreamtcAbapstructtcAarraytcTexttcShorttexttcBintext" _typeCode_name_3 = "tcAlphanum" _typeCode_name_4 = "tcLongdatetcSeconddatetcDaydatetcSecondtime" _typeCode_name_5 = "tcClocatortcBlobDiskReservedtcClobDiskReservedtcNclobDiskReservedtcStGeometrytcStPointtcFixed16tcAbapItabtcRecordRowStoretcRecordColumnStore" _typeCode_name_6 = "tcFixed8tcFixed12" _typeCode_name_7 = "tcCiphertext" _typeCode_name_8 = "tcTableReftcTableRows" ) var ( _typeCode_index_0 = [...]uint16{0, 7, 16, 26, 35, 43, 52, 58, 66, 72, 81, 88, 98, 106, 117, 123, 129, 140, 148, 157, 170, 184, 196, 208, 215, 223, 229, 236, 242, 251, 259, 268, 277, 287, 296, 315, 325} _typeCode_index_2 = [...]uint8{0, 14, 26, 38, 46, 52, 63, 72} _typeCode_index_4 = [...]uint8{0, 10, 22, 31, 43} _typeCode_index_5 = [...]uint8{0, 10, 28, 46, 65, 77, 86, 95, 105, 121, 140} _typeCode_index_6 = [...]uint8{0, 8, 17} _typeCode_index_8 = [...]uint8{0, 10, 21} ) func (i typeCode) String() string { switch { case i <= 35: return _typeCode_name_0[_typeCode_index_0[i]:_typeCode_index_0[i+1]] case i == 45: return _typeCode_name_1 case 47 <= i && i <= 53: i -= 47 return _typeCode_name_2[_typeCode_index_2[i]:_typeCode_index_2[i+1]] case i == 55: return _typeCode_name_3 case 61 <= i && i <= 64: i -= 61 return _typeCode_name_4[_typeCode_index_4[i]:_typeCode_index_4[i+1]] case 70 <= i && i <= 79: i -= 70 return _typeCode_name_5[_typeCode_index_5[i]:_typeCode_index_5[i+1]] case 81 <= i && i <= 82: i -= 81 return _typeCode_name_6[_typeCode_index_6[i]:_typeCode_index_6[i+1]] case i == 90: return _typeCode_name_7 case 126 <= i && i <= 127: i -= 126 return _typeCode_name_8[_typeCode_index_8[i]:_typeCode_index_8[i+1]] default: return "typeCode(" + strconv.FormatInt(int64(i), 10) + ")" } } golang-github-sap-go-hdb-0.100.10/internal/unicode/000077500000000000000000000000001370256154600216525ustar00rootroot00000000000000golang-github-sap-go-hdb-0.100.10/internal/unicode/cesu8/000077500000000000000000000000001370256154600227015ustar00rootroot00000000000000golang-github-sap-go-hdb-0.100.10/internal/unicode/cesu8/cesu8.go000066400000000000000000000123711370256154600242630ustar00rootroot00000000000000/* 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.100.10/internal/unicode/cesu8/cesu8_test.go000066400000000000000000000050671370256154600253260ustar00rootroot00000000000000/* 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{ {0x45, []byte{0x45}}, {0x205, []byte{0xc8, 0x85}}, {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.Equal(b[:n1], d.utf8) { 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.100.10/internal/unicode/unicode.go000066400000000000000000000056361370256154600236410ustar00rootroot00000000000000/* 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.100.10/trust.pem000066400000000000000000000023651370256154600203020ustar00rootroot00000000000000-----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-----