From 2aa3395f804a3e6085f020615add44bbf2e51dce Mon Sep 17 00:00:00 2001 From: Julien Date: Wed, 9 Jan 2013 00:56:56 +0100 Subject: [PATCH 1/3] Update Go-MySQL-Driver repository and remove local packages --- src/sqltest/drivers.go => drivers.go | 2 +- src/sqltest/sql_test.go => sql_test.go | 0 .../p/go-mysql-driver/mysql/LICENSE | 373 ------ .../p/go-mysql-driver/mysql/connection.go | 286 ----- .../p/go-mysql-driver/mysql/const.go | 129 -- .../p/go-mysql-driver/mysql/driver.go | 72 -- .../p/go-mysql-driver/mysql/packets.go | 1012 ---------------- .../p/go-mysql-driver/mysql/result.go | 22 - .../p/go-mysql-driver/mysql/rows.go | 58 - .../p/go-mysql-driver/mysql/statement.go | 128 -- .../p/go-mysql-driver/mysql/transaction.go | 25 - .../p/go-mysql-driver/mysql/utils.go | 316 ----- src/github.com/bmizerany/pq/.gitignore | 2 - src/github.com/bmizerany/pq/README.md | 67 -- src/github.com/bmizerany/pq/buf.go | 74 -- src/github.com/bmizerany/pq/conn.go | 486 -------- src/github.com/bmizerany/pq/conn_test.go | 299 ----- src/github.com/bmizerany/pq/encode.go | 83 -- src/github.com/bmizerany/pq/encode_test.go | 26 - src/github.com/bmizerany/pq/error.go | 78 -- src/github.com/bmizerany/pq/types.go | 317 ----- src/github.com/bmizerany/pq/url.go | 69 -- src/github.com/bmizerany/pq/url_test.go | 53 - src/github.com/mattn/go-sqlite3/Makefile | 6 - src/github.com/mattn/go-sqlite3/README.mkd | 12 - .../mattn/go-sqlite3/example/Makefile | 6 - .../mattn/go-sqlite3/example/main.go | 65 - src/github.com/mattn/go-sqlite3/sqlite3.go | 282 ----- .../mattn/go-sqlite3/sqlite3_test.go | 262 ----- src/github.com/ziutek/mymysql/.gitignore | 10 - src/github.com/ziutek/mymysql/LICENSE | 24 - src/github.com/ziutek/mymysql/Makefile | 13 - src/github.com/ziutek/mymysql/README.md | 591 ---------- src/github.com/ziutek/mymysql/autorc/LICENSE | 24 - src/github.com/ziutek/mymysql/autorc/Makefile | 7 - .../ziutek/mymysql/autorc/autorecon.go | 137 --- .../ziutek/mymysql/autorc/autorecon_test.go | 103 -- src/github.com/ziutek/mymysql/godrv/Makefile | 7 - src/github.com/ziutek/mymysql/godrv/driver.go | 217 ---- .../ziutek/mymysql/godrv/driver_test.go | 75 -- src/github.com/ziutek/mymysql/make.bash | 5 - src/github.com/ziutek/mymysql/mysql/Makefile | 12 - src/github.com/ziutek/mymysql/mysql/errors.go | 494 -------- src/github.com/ziutek/mymysql/mysql/field.go | 15 - .../ziutek/mymysql/mysql/interface.go | 69 -- src/github.com/ziutek/mymysql/mysql/row.go | 432 ------- src/github.com/ziutek/mymysql/mysql/types.go | 214 ---- .../ziutek/mymysql/mysql/types_test.go | 82 -- src/github.com/ziutek/mymysql/mysql/utils.go | 52 - src/github.com/ziutek/mymysql/native/LICENSE | 24 - src/github.com/ziutek/mymysql/native/Makefile | 19 - .../ziutek/mymysql/native/addons.go | 17 - .../ziutek/mymysql/native/bind_test.go | 316 ----- .../ziutek/mymysql/native/binding.go | 151 --- .../ziutek/mymysql/native/codecs.go | 523 --------- .../ziutek/mymysql/native/command.go | 101 -- .../ziutek/mymysql/native/common.go | 86 -- .../ziutek/mymysql/native/consts.go | 192 --- .../ziutek/mymysql/native/errors.go | 31 - src/github.com/ziutek/mymysql/native/init.go | 65 - src/github.com/ziutek/mymysql/native/mysql.go | 729 ------------ .../ziutek/mymysql/native/native_test.go | 1041 ----------------- .../ziutek/mymysql/native/packet.go | 154 --- .../ziutek/mymysql/native/prepared.go | 168 --- .../ziutek/mymysql/native/result.go | 325 ----- .../ziutek/mymysql/native/unsafe.go | 116 -- src/github.com/ziutek/mymysql/thrsafe/LICENSE | 24 - .../ziutek/mymysql/thrsafe/Makefile | 7 - .../ziutek/mymysql/thrsafe/thrsafe.go | 236 ---- .../ziutek/mymysql/thrsafe/thrsafe_test.go | 1 - 70 files changed, 1 insertion(+), 11518 deletions(-) rename src/sqltest/drivers.go => drivers.go (73%) rename src/sqltest/sql_test.go => sql_test.go (100%) delete mode 100644 src/code.google.com/p/go-mysql-driver/mysql/LICENSE delete mode 100644 src/code.google.com/p/go-mysql-driver/mysql/connection.go delete mode 100644 src/code.google.com/p/go-mysql-driver/mysql/const.go delete mode 100644 src/code.google.com/p/go-mysql-driver/mysql/driver.go delete mode 100644 src/code.google.com/p/go-mysql-driver/mysql/packets.go delete mode 100644 src/code.google.com/p/go-mysql-driver/mysql/result.go delete mode 100644 src/code.google.com/p/go-mysql-driver/mysql/rows.go delete mode 100644 src/code.google.com/p/go-mysql-driver/mysql/statement.go delete mode 100644 src/code.google.com/p/go-mysql-driver/mysql/transaction.go delete mode 100644 src/code.google.com/p/go-mysql-driver/mysql/utils.go delete mode 100644 src/github.com/bmizerany/pq/.gitignore delete mode 100644 src/github.com/bmizerany/pq/README.md delete mode 100644 src/github.com/bmizerany/pq/buf.go delete mode 100644 src/github.com/bmizerany/pq/conn.go delete mode 100644 src/github.com/bmizerany/pq/conn_test.go delete mode 100644 src/github.com/bmizerany/pq/encode.go delete mode 100644 src/github.com/bmizerany/pq/encode_test.go delete mode 100644 src/github.com/bmizerany/pq/error.go delete mode 100644 src/github.com/bmizerany/pq/types.go delete mode 100644 src/github.com/bmizerany/pq/url.go delete mode 100644 src/github.com/bmizerany/pq/url_test.go delete mode 100644 src/github.com/mattn/go-sqlite3/Makefile delete mode 100644 src/github.com/mattn/go-sqlite3/README.mkd delete mode 100644 src/github.com/mattn/go-sqlite3/example/Makefile delete mode 100644 src/github.com/mattn/go-sqlite3/example/main.go delete mode 100644 src/github.com/mattn/go-sqlite3/sqlite3.go delete mode 100644 src/github.com/mattn/go-sqlite3/sqlite3_test.go delete mode 100644 src/github.com/ziutek/mymysql/.gitignore delete mode 100644 src/github.com/ziutek/mymysql/LICENSE delete mode 100644 src/github.com/ziutek/mymysql/Makefile delete mode 100644 src/github.com/ziutek/mymysql/README.md delete mode 100644 src/github.com/ziutek/mymysql/autorc/LICENSE delete mode 100644 src/github.com/ziutek/mymysql/autorc/Makefile delete mode 100644 src/github.com/ziutek/mymysql/autorc/autorecon.go delete mode 100644 src/github.com/ziutek/mymysql/autorc/autorecon_test.go delete mode 100644 src/github.com/ziutek/mymysql/godrv/Makefile delete mode 100644 src/github.com/ziutek/mymysql/godrv/driver.go delete mode 100644 src/github.com/ziutek/mymysql/godrv/driver_test.go delete mode 100755 src/github.com/ziutek/mymysql/make.bash delete mode 100644 src/github.com/ziutek/mymysql/mysql/Makefile delete mode 100644 src/github.com/ziutek/mymysql/mysql/errors.go delete mode 100644 src/github.com/ziutek/mymysql/mysql/field.go delete mode 100644 src/github.com/ziutek/mymysql/mysql/interface.go delete mode 100644 src/github.com/ziutek/mymysql/mysql/row.go delete mode 100644 src/github.com/ziutek/mymysql/mysql/types.go delete mode 100644 src/github.com/ziutek/mymysql/mysql/types_test.go delete mode 100644 src/github.com/ziutek/mymysql/mysql/utils.go delete mode 100644 src/github.com/ziutek/mymysql/native/LICENSE delete mode 100644 src/github.com/ziutek/mymysql/native/Makefile delete mode 100644 src/github.com/ziutek/mymysql/native/addons.go delete mode 100644 src/github.com/ziutek/mymysql/native/bind_test.go delete mode 100644 src/github.com/ziutek/mymysql/native/binding.go delete mode 100644 src/github.com/ziutek/mymysql/native/codecs.go delete mode 100644 src/github.com/ziutek/mymysql/native/command.go delete mode 100644 src/github.com/ziutek/mymysql/native/common.go delete mode 100644 src/github.com/ziutek/mymysql/native/consts.go delete mode 100644 src/github.com/ziutek/mymysql/native/errors.go delete mode 100644 src/github.com/ziutek/mymysql/native/init.go delete mode 100644 src/github.com/ziutek/mymysql/native/mysql.go delete mode 100644 src/github.com/ziutek/mymysql/native/native_test.go delete mode 100644 src/github.com/ziutek/mymysql/native/packet.go delete mode 100644 src/github.com/ziutek/mymysql/native/prepared.go delete mode 100644 src/github.com/ziutek/mymysql/native/result.go delete mode 100644 src/github.com/ziutek/mymysql/native/unsafe.go delete mode 100644 src/github.com/ziutek/mymysql/thrsafe/LICENSE delete mode 100644 src/github.com/ziutek/mymysql/thrsafe/Makefile delete mode 100644 src/github.com/ziutek/mymysql/thrsafe/thrsafe.go delete mode 100644 src/github.com/ziutek/mymysql/thrsafe/thrsafe_test.go diff --git a/src/sqltest/drivers.go b/drivers.go similarity index 73% rename from src/sqltest/drivers.go rename to drivers.go index e8433f7..eb7cd6f 100644 --- a/src/sqltest/drivers.go +++ b/drivers.go @@ -3,6 +3,6 @@ package sqltest import ( _ "github.com/mattn/go-sqlite3" _ "github.com/ziutek/mymysql/godrv" - _ "code.google.com/p/go-mysql-driver/mysql" + _ "github.com/Go-SQL-Driver/MySQL" _ "github.com/bmizerany/pq" ) diff --git a/src/sqltest/sql_test.go b/sql_test.go similarity index 100% rename from src/sqltest/sql_test.go rename to sql_test.go diff --git a/src/code.google.com/p/go-mysql-driver/mysql/LICENSE b/src/code.google.com/p/go-mysql-driver/mysql/LICENSE deleted file mode 100644 index 14e2f77..0000000 --- a/src/code.google.com/p/go-mysql-driver/mysql/LICENSE +++ /dev/null @@ -1,373 +0,0 @@ -Mozilla Public License Version 2.0 -================================== - -1. Definitions --------------- - -1.1. "Contributor" - means each individual or legal entity that creates, contributes to - the creation of, or owns Covered Software. - -1.2. "Contributor Version" - means the combination of the Contributions of others (if any) used - by a Contributor and that particular Contributor's Contribution. - -1.3. "Contribution" - means Covered Software of a particular Contributor. - -1.4. "Covered Software" - means Source Code Form to which the initial Contributor has attached - the notice in Exhibit A, the Executable Form of such Source Code - Form, and Modifications of such Source Code Form, in each case - including portions thereof. - -1.5. "Incompatible With Secondary Licenses" - means - - (a) that the initial Contributor has attached the notice described - in Exhibit B to the Covered Software; or - - (b) that the Covered Software was made available under the terms of - version 1.1 or earlier of the License, but not also under the - terms of a Secondary License. - -1.6. "Executable Form" - means any form of the work other than Source Code Form. - -1.7. "Larger Work" - means a work that combines Covered Software with other material, in - a separate file or files, that is not Covered Software. - -1.8. "License" - means this document. - -1.9. "Licensable" - means having the right to grant, to the maximum extent possible, - whether at the time of the initial grant or subsequently, any and - all of the rights conveyed by this License. - -1.10. "Modifications" - means any of the following: - - (a) any file in Source Code Form that results from an addition to, - deletion from, or modification of the contents of Covered - Software; or - - (b) any new file in Source Code Form that contains any Covered - Software. - -1.11. "Patent Claims" of a Contributor - means any patent claim(s), including without limitation, method, - process, and apparatus claims, in any patent Licensable by such - Contributor that would be infringed, but for the grant of the - License, by the making, using, selling, offering for sale, having - made, import, or transfer of either its Contributions or its - Contributor Version. - -1.12. "Secondary License" - means either the GNU General Public License, Version 2.0, the GNU - Lesser General Public License, Version 2.1, the GNU Affero General - Public License, Version 3.0, or any later versions of those - licenses. - -1.13. "Source Code Form" - means the form of the work preferred for making modifications. - -1.14. "You" (or "Your") - means an individual or a legal entity exercising rights under this - License. For legal entities, "You" includes any entity that - controls, is controlled by, or is under common control with You. For - purposes of this definition, "control" means (a) the power, direct - or indirect, to cause the direction or management of such entity, - whether by contract or otherwise, or (b) ownership of more than - fifty percent (50%) of the outstanding shares or beneficial - ownership of such entity. - -2. License Grants and Conditions --------------------------------- - -2.1. Grants - -Each Contributor hereby grants You a world-wide, royalty-free, -non-exclusive license: - -(a) under intellectual property rights (other than patent or trademark) - Licensable by such Contributor to use, reproduce, make available, - modify, display, perform, distribute, and otherwise exploit its - Contributions, either on an unmodified basis, with Modifications, or - as part of a Larger Work; and - -(b) under Patent Claims of such Contributor to make, use, sell, offer - for sale, have made, import, and otherwise transfer either its - Contributions or its Contributor Version. - -2.2. Effective Date - -The licenses granted in Section 2.1 with respect to any Contribution -become effective for each Contribution on the date the Contributor first -distributes such Contribution. - -2.3. Limitations on Grant Scope - -The licenses granted in this Section 2 are the only rights granted under -this License. No additional rights or licenses will be implied from the -distribution or licensing of Covered Software under this License. -Notwithstanding Section 2.1(b) above, no patent license is granted by a -Contributor: - -(a) for any code that a Contributor has removed from Covered Software; - or - -(b) for infringements caused by: (i) Your and any other third party's - modifications of Covered Software, or (ii) the combination of its - Contributions with other software (except as part of its Contributor - Version); or - -(c) under Patent Claims infringed by Covered Software in the absence of - its Contributions. - -This License does not grant any rights in the trademarks, service marks, -or logos of any Contributor (except as may be necessary to comply with -the notice requirements in Section 3.4). - -2.4. Subsequent Licenses - -No Contributor makes additional grants as a result of Your choice to -distribute the Covered Software under a subsequent version of this -License (see Section 10.2) or under the terms of a Secondary License (if -permitted under the terms of Section 3.3). - -2.5. Representation - -Each Contributor represents that the Contributor believes its -Contributions are its original creation(s) or it has sufficient rights -to grant the rights to its Contributions conveyed by this License. - -2.6. Fair Use - -This License is not intended to limit any rights You have under -applicable copyright doctrines of fair use, fair dealing, or other -equivalents. - -2.7. Conditions - -Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted -in Section 2.1. - -3. Responsibilities -------------------- - -3.1. Distribution of Source Form - -All distribution of Covered Software in Source Code Form, including any -Modifications that You create or to which You contribute, must be under -the terms of this License. You must inform recipients that the Source -Code Form of the Covered Software is governed by the terms of this -License, and how they can obtain a copy of this License. You may not -attempt to alter or restrict the recipients' rights in the Source Code -Form. - -3.2. Distribution of Executable Form - -If You distribute Covered Software in Executable Form then: - -(a) such Covered Software must also be made available in Source Code - Form, as described in Section 3.1, and You must inform recipients of - the Executable Form how they can obtain a copy of such Source Code - Form by reasonable means in a timely manner, at a charge no more - than the cost of distribution to the recipient; and - -(b) You may distribute such Executable Form under the terms of this - License, or sublicense it under different terms, provided that the - license for the Executable Form does not attempt to limit or alter - the recipients' rights in the Source Code Form under this License. - -3.3. Distribution of a Larger Work - -You may create and distribute a Larger Work under terms of Your choice, -provided that You also comply with the requirements of this License for -the Covered Software. If the Larger Work is a combination of Covered -Software with a work governed by one or more Secondary Licenses, and the -Covered Software is not Incompatible With Secondary Licenses, this -License permits You to additionally distribute such Covered Software -under the terms of such Secondary License(s), so that the recipient of -the Larger Work may, at their option, further distribute the Covered -Software under the terms of either this License or such Secondary -License(s). - -3.4. Notices - -You may not remove or alter the substance of any license notices -(including copyright notices, patent notices, disclaimers of warranty, -or limitations of liability) contained within the Source Code Form of -the Covered Software, except that You may alter any license notices to -the extent required to remedy known factual inaccuracies. - -3.5. Application of Additional Terms - -You may choose to offer, and to charge a fee for, warranty, support, -indemnity or liability obligations to one or more recipients of Covered -Software. However, You may do so only on Your own behalf, and not on -behalf of any Contributor. You must make it absolutely clear that any -such warranty, support, indemnity, or liability obligation is offered by -You alone, and You hereby agree to indemnify every Contributor for any -liability incurred by such Contributor as a result of warranty, support, -indemnity or liability terms You offer. You may include additional -disclaimers of warranty and limitations of liability specific to any -jurisdiction. - -4. Inability to Comply Due to Statute or Regulation ---------------------------------------------------- - -If it is impossible for You to comply with any of the terms of this -License with respect to some or all of the Covered Software due to -statute, judicial order, or regulation then You must: (a) comply with -the terms of this License to the maximum extent possible; and (b) -describe the limitations and the code they affect. Such description must -be placed in a text file included with all distributions of the Covered -Software under this License. Except to the extent prohibited by statute -or regulation, such description must be sufficiently detailed for a -recipient of ordinary skill to be able to understand it. - -5. Termination --------------- - -5.1. The rights granted under this License will terminate automatically -if You fail to comply with any of its terms. However, if You become -compliant, then the rights granted under this License from a particular -Contributor are reinstated (a) provisionally, unless and until such -Contributor explicitly and finally terminates Your grants, and (b) on an -ongoing basis, if such Contributor fails to notify You of the -non-compliance by some reasonable means prior to 60 days after You have -come back into compliance. Moreover, Your grants from a particular -Contributor are reinstated on an ongoing basis if such Contributor -notifies You of the non-compliance by some reasonable means, this is the -first time You have received notice of non-compliance with this License -from such Contributor, and You become compliant prior to 30 days after -Your receipt of the notice. - -5.2. If You initiate litigation against any entity by asserting a patent -infringement claim (excluding declaratory judgment actions, -counter-claims, and cross-claims) alleging that a Contributor Version -directly or indirectly infringes any patent, then the rights granted to -You by any and all Contributors for the Covered Software under Section -2.1 of this License shall terminate. - -5.3. In the event of termination under Sections 5.1 or 5.2 above, all -end user license agreements (excluding distributors and resellers) which -have been validly granted by You or Your distributors under this License -prior to termination shall survive termination. - -************************************************************************ -* * -* 6. Disclaimer of Warranty * -* ------------------------- * -* * -* Covered Software is provided under this License on an "as is" * -* basis, without warranty of any kind, either expressed, implied, or * -* statutory, including, without limitation, warranties that the * -* Covered Software is free of defects, merchantable, fit for a * -* particular purpose or non-infringing. The entire risk as to the * -* quality and performance of the Covered Software is with You. * -* Should any Covered Software prove defective in any respect, You * -* (not any Contributor) assume the cost of any necessary servicing, * -* repair, or correction. This disclaimer of warranty constitutes an * -* essential part of this License. No use of any Covered Software is * -* authorized under this License except under this disclaimer. * -* * -************************************************************************ - -************************************************************************ -* * -* 7. Limitation of Liability * -* -------------------------- * -* * -* Under no circumstances and under no legal theory, whether tort * -* (including negligence), contract, or otherwise, shall any * -* Contributor, or anyone who distributes Covered Software as * -* permitted above, be liable to You for any direct, indirect, * -* special, incidental, or consequential damages of any character * -* including, without limitation, damages for lost profits, loss of * -* goodwill, work stoppage, computer failure or malfunction, or any * -* and all other commercial damages or losses, even if such party * -* shall have been informed of the possibility of such damages. This * -* limitation of liability shall not apply to liability for death or * -* personal injury resulting from such party's negligence to the * -* extent applicable law prohibits such limitation. Some * -* jurisdictions do not allow the exclusion or limitation of * -* incidental or consequential damages, so this exclusion and * -* limitation may not apply to You. * -* * -************************************************************************ - -8. Litigation -------------- - -Any litigation relating to this License may be brought only in the -courts of a jurisdiction where the defendant maintains its principal -place of business and such litigation shall be governed by laws of that -jurisdiction, without reference to its conflict-of-law provisions. -Nothing in this Section shall prevent a party's ability to bring -cross-claims or counter-claims. - -9. Miscellaneous ----------------- - -This License represents the complete agreement concerning the subject -matter hereof. If any provision of this License is held to be -unenforceable, such provision shall be reformed only to the extent -necessary to make it enforceable. Any law or regulation which provides -that the language of a contract shall be construed against the drafter -shall not be used to construe this License against a Contributor. - -10. Versions of the License ---------------------------- - -10.1. New Versions - -Mozilla Foundation is the license steward. Except as provided in Section -10.3, no one other than the license steward has the right to modify or -publish new versions of this License. Each version will be given a -distinguishing version number. - -10.2. Effect of New Versions - -You may distribute the Covered Software under the terms of the version -of the License under which You originally received the Covered Software, -or under the terms of any subsequent version published by the license -steward. - -10.3. Modified Versions - -If you create software not governed by this License, and you want to -create a new license for such software, you may create and use a -modified version of this License if you rename the license and remove -any references to the name of the license steward (except to note that -such modified license differs from this License). - -10.4. Distributing Source Code Form that is Incompatible With Secondary -Licenses - -If You choose to distribute Source Code Form that is Incompatible With -Secondary Licenses under the terms of this version of the License, the -notice described in Exhibit B of this License must be attached. - -Exhibit A - Source Code Form License Notice -------------------------------------------- - - This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.0. If a copy of the MPL was not distributed with this - file, You can obtain one at http://mozilla.org/MPL/2.0/. - -If it is not possible or desirable to put the notice in a particular -file, then You may include the notice in a location (such as a LICENSE -file in a relevant directory) where a recipient would be likely to look -for such a notice. - -You may add additional accurate notices of copyright ownership. - -Exhibit B - "Incompatible With Secondary Licenses" Notice ---------------------------------------------------------- - - This Source Code Form is "Incompatible With Secondary Licenses", as - defined by the Mozilla Public License, v. 2.0. diff --git a/src/code.google.com/p/go-mysql-driver/mysql/connection.go b/src/code.google.com/p/go-mysql-driver/mysql/connection.go deleted file mode 100644 index a5541d8..0000000 --- a/src/code.google.com/p/go-mysql-driver/mysql/connection.go +++ /dev/null @@ -1,286 +0,0 @@ -// Go MySQL Driver - A MySQL-Driver for Go's database/sql package -// -// Copyright 2012 Julien Schmidt. All rights reserved. -// http://www.julienschmidt.com -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this file, -// You can obtain one at http://mozilla.org/MPL/2.0/. -package mysql - -import ( - "bufio" - "database/sql/driver" - "errors" - "net" - "strconv" - "time" -) - -type mysqlConn struct { - cfg *config - server *serverSettings - netConn net.Conn - bufReader *bufio.Reader - protocol uint8 - sequence uint8 - affectedRows uint64 - insertId uint64 - lastCmdTime time.Time - keepaliveTimer *time.Timer -} - -type config struct { - user string - passwd string - net string - addr string - dbname string - params map[string]string -} - -type serverSettings struct { - protocol byte - version string - flags ClientFlag - charset uint8 - scrambleBuff []byte - threadID uint32 - keepalive int64 -} - -// Handles parameters set in DSN -func (mc *mysqlConn) handleParams() (e error) { - for param, val := range mc.cfg.params { - switch param { - // Charset - case "charset": - e = mc.exec("SET NAMES " + val) - if e != nil { - return - } - - // TLS-Encryption - case "tls": - dbgLog.Print("TLS-Encryption not implemented yet") - - // Compression - case "compress": - dbgLog.Print("Compression not implemented yet") - - // We don't want to set keepalive as system var - case "keepalive": - continue - - // System Vars - default: - e = mc.exec("SET " + param + "=" + val + "") - if e != nil { - return - } - } - } - - // KeepAlive - if val, param := mc.cfg.params["keepalive"]; param { - mc.server.keepalive, e = strconv.ParseInt(val, 10, 64) - if e != nil { - return errors.New("Invalid keepalive time") - } - - // Get keepalive time by MySQL system var wait_timeout - if mc.server.keepalive == 1 { - val, e = mc.getSystemVar("wait_timeout") - mc.server.keepalive, e = strconv.ParseInt(val, 10, 64) - if e != nil { - return errors.New("Error getting wait_timeout") - } - - // Trigger 1min BEFORE wait_timeout - if mc.server.keepalive > 60 { - mc.server.keepalive -= 60 - } - } - - if mc.server.keepalive > 0 { - mc.lastCmdTime = time.Now() - - // Ping-Timer to avoid timeout - mc.keepaliveTimer = time.AfterFunc( - time.Duration(mc.server.keepalive)*time.Second, func() { - var diff time.Duration - for { - // Fires only if diff > keepalive. Makes it collision safe - for mc.netConn != nil && - mc.lastCmdTime.Unix()+mc.server.keepalive > time.Now().Unix() { - diff = mc.lastCmdTime.Sub(time.Unix(time.Now().Unix()-mc.server.keepalive, 0)) - time.Sleep(diff) - } - if mc.netConn != nil { - if e := mc.Ping(); e != nil { - break - } - } else { - return - } - } - }) - } - } - return -} - -func (mc *mysqlConn) Begin() (driver.Tx, error) { - e := mc.exec("START TRANSACTION") - if e != nil { - return nil, e - } - - return &mysqlTx{mc}, e -} - -func (mc *mysqlConn) Close() (e error) { - if mc.server.keepalive > 0 { - mc.keepaliveTimer.Stop() - } - mc.writeCommandPacket(COM_QUIT) - mc.bufReader = nil - mc.netConn.Close() - mc.netConn = nil - return -} - -func (mc *mysqlConn) Prepare(query string) (driver.Stmt, error) { - // Send command - e := mc.writeCommandPacket(COM_STMT_PREPARE, query) - if e != nil { - return nil, e - } - - stmt := mysqlStmt{new(stmtContent)} - stmt.mc = mc - - // Read Result - var columnCount uint16 - columnCount, e = stmt.readPrepareResultPacket() - if e != nil { - return nil, e - } - - if stmt.paramCount > 0 { - stmt.params, e = stmt.mc.readColumns(stmt.paramCount) - if e != nil { - return nil, e - } - } - - if columnCount > 0 { - _, e = stmt.mc.readUntilEOF() - if e != nil { - return nil, e - } - } - - return stmt, e -} - -func (mc *mysqlConn) Exec(query string, args []driver.Value) (driver.Result, error) { - if len(args) > 0 { - return nil, driver.ErrSkip - } - - mc.affectedRows = 0 - mc.insertId = 0 - - e := mc.exec(query) - if e != nil { - return nil, e - } - - if mc.affectedRows == 0 { - return driver.ResultNoRows, e - } - - return &mysqlResult{ - affectedRows: int64(mc.affectedRows), - insertId: int64(mc.insertId)}, - e -} - -// Internal function to execute statements -func (mc *mysqlConn) exec(query string) (e error) { - // Send command - e = mc.writeCommandPacket(COM_QUERY, query) - if e != nil { - return - } - - // Read Result - resLen, e := mc.readResultSetHeaderPacket() - if e != nil { - return - } - - mc.affectedRows = 0 - mc.insertId = 0 - - if resLen > 0 { - _, e = mc.readUntilEOF() - if e != nil { - return - } - - mc.affectedRows, e = mc.readUntilEOF() - if e != nil { - return - } - } - - return -} - -// Gets the value of the given MySQL System Variable -func (mc *mysqlConn) getSystemVar(name string) (val string, e error) { - // Send command - e = mc.writeCommandPacket(COM_QUERY, "SELECT @@"+name) - if e != nil { - return - } - - // Read Result - resLen, e := mc.readResultSetHeaderPacket() - if e != nil { - return - } - - if resLen > 0 { - var n uint64 - n, e = mc.readUntilEOF() - if e != nil { - return - } - - var rows []*[][]byte - rows, e = mc.readRows(int(n)) - if e != nil { - return - } - - val = string((*rows[0])[0]) - } - - return -} - -// Executes a simple Ping-CMD to test or keepalive the connection -func (mc *mysqlConn) Ping() (e error) { - // Send command - e = mc.writeCommandPacket(COM_PING) - if e != nil { - return - } - - // Read Result - e = mc.readResultOK() - return -} diff --git a/src/code.google.com/p/go-mysql-driver/mysql/const.go b/src/code.google.com/p/go-mysql-driver/mysql/const.go deleted file mode 100644 index 1b2e615..0000000 --- a/src/code.google.com/p/go-mysql-driver/mysql/const.go +++ /dev/null @@ -1,129 +0,0 @@ -// Go MySQL Driver - A MySQL-Driver for Go's database/sql package -// -// Copyright 2012 Julien Schmidt. All rights reserved. -// http://www.julienschmidt.com -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this file, -// You can obtain one at http://mozilla.org/MPL/2.0/. -package mysql - -// Constants documentation: -// http://forge.mysql.com/wiki/MySQL_Internals_ClientServer_Protocol - -const ( - MIN_PROTOCOL_VERSION = 10 - MAX_PACKET_SIZE = 1<<24 - 1 - TIME_FORMAT = "2006-01-02 15:04:05" -) - -type ClientFlag uint32 - -const ( - CLIENT_LONG_PASSWORD ClientFlag = 1 << iota - CLIENT_FOUND_ROWS - CLIENT_LONG_FLAG - CLIENT_CONNECT_WITH_DB - CLIENT_NO_SCHEMA - CLIENT_COMPRESS - CLIENT_ODBC - CLIENT_LOCAL_FILES - CLIENT_IGNORE_SPACE - CLIENT_PROTOCOL_41 - CLIENT_INTERACTIVE - CLIENT_SSL - CLIENT_IGNORE_SIGPIPE - CLIENT_TRANSACTIONS - CLIENT_RESERVED - CLIENT_SECURE_CONN - CLIENT_MULTI_STATEMENTS - CLIENT_MULTI_RESULTS -) - -type commandType byte - -const ( - COM_QUIT commandType = iota + 1 - COM_INIT_DB - COM_QUERY - COM_FIELD_LIST - COM_CREATE_DB - COM_DROP_DB - COM_REFRESH - COM_SHUTDOWN - COM_STATISTICS - COM_PROCESS_INFO - COM_CONNECT - COM_PROCESS_KILL - COM_DEBUG - COM_PING - COM_TIME - COM_DELAYED_INSERT - COM_CHANGE_USER - COM_BINLOG_DUMP - COM_TABLE_DUMP - COM_CONNECT_OUT - COM_REGISTER_SLAVE - COM_STMT_PREPARE - COM_STMT_EXECUTE - COM_STMT_SEND_LONG_DATA - COM_STMT_CLOSE - COM_STMT_RESET - COM_SET_OPTION - COM_STMT_FETCH -) - -type FieldType byte - -const ( - FIELD_TYPE_DECIMAL FieldType = iota - FIELD_TYPE_TINY - FIELD_TYPE_SHORT - FIELD_TYPE_LONG - FIELD_TYPE_FLOAT - FIELD_TYPE_DOUBLE - FIELD_TYPE_NULL - FIELD_TYPE_TIMESTAMP - FIELD_TYPE_LONGLONG - FIELD_TYPE_INT24 - FIELD_TYPE_DATE - FIELD_TYPE_TIME - FIELD_TYPE_DATETIME - FIELD_TYPE_YEAR - FIELD_TYPE_NEWDATE - FIELD_TYPE_VARCHAR - FIELD_TYPE_BIT -) -const ( - FIELD_TYPE_NEWDECIMAL FieldType = iota + 0xf6 - FIELD_TYPE_ENUM - FIELD_TYPE_SET - FIELD_TYPE_TINY_BLOB - FIELD_TYPE_MEDIUM_BLOB - FIELD_TYPE_LONG_BLOB - FIELD_TYPE_BLOB - FIELD_TYPE_VAR_STRING - FIELD_TYPE_STRING - FIELD_TYPE_GEOMETRY -) - -type FieldFlag uint16 - -const ( - FLAG_NOT_NULL FieldFlag = 1 << iota - FLAG_PRI_KEY - FLAG_UNIQUE_KEY - FLAG_MULTIPLE_KEY - FLAG_BLOB - FLAG_UNSIGNED - FLAG_ZEROFILL - FLAG_BINARY - FLAG_ENUM - FLAG_AUTO_INCREMENT - FLAG_TIMESTAMP - FLAG_SET - FLAG_UNKNOWN_1 - FLAG_UNKNOWN_2 - FLAG_UNKNOWN_3 - FLAG_UNKNOWN_4 -) diff --git a/src/code.google.com/p/go-mysql-driver/mysql/driver.go b/src/code.google.com/p/go-mysql-driver/mysql/driver.go deleted file mode 100644 index 33df973..0000000 --- a/src/code.google.com/p/go-mysql-driver/mysql/driver.go +++ /dev/null @@ -1,72 +0,0 @@ -// Go MySQL Driver - A MySQL-Driver for Go's database/sql package -// -// Copyright 2012 Julien Schmidt. All rights reserved. -// http://www.julienschmidt.com -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this file, -// You can obtain one at http://mozilla.org/MPL/2.0/. -package mysql - -import ( - "bufio" - "database/sql" - "database/sql/driver" - "errors" - "net" -) - -type mysqlDriver struct{} - -// Open new Connection. -// See http://code.google.com/p/go-mysql-driver/#DSN_(Data_Source_Name) for how -// the DSN string is formated -func (d *mysqlDriver) Open(dsn string) (driver.Conn, error) { - var e error - - // New mysqlConn - mc := new(mysqlConn) - mc.cfg = parseDSN(dsn) - - if mc.cfg.dbname == "" { - e = errors.New("Incomplete or invalid DSN") - return nil, e - } - - // Connect to Server - mc.netConn, e = net.Dial(mc.cfg.net, mc.cfg.addr) - if e != nil { - return nil, e - } - mc.bufReader = bufio.NewReader(mc.netConn) - - // Reading Handshake Initialization Packet - e = mc.readInitPacket() - if e != nil { - return nil, e - } - - // Send Client Authentication Packet - e = mc.writeAuthPacket() - if e != nil { - return nil, e - } - - // Read Result Packet - e = mc.readResultOK() - if e != nil { - return nil, e - } - - // Handle DSN Params - e = mc.handleParams() - if e != nil { - return nil, e - } - - return mc, e -} - -func init() { - sql.Register("mysql", &mysqlDriver{}) -} diff --git a/src/code.google.com/p/go-mysql-driver/mysql/packets.go b/src/code.google.com/p/go-mysql-driver/mysql/packets.go deleted file mode 100644 index 407c14d..0000000 --- a/src/code.google.com/p/go-mysql-driver/mysql/packets.go +++ /dev/null @@ -1,1012 +0,0 @@ -// Go MySQL Driver - A MySQL-Driver for Go's database/sql package -// -// Copyright 2012 Julien Schmidt. All rights reserved. -// http://www.julienschmidt.com -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this file, -// You can obtain one at http://mozilla.org/MPL/2.0/. -package mysql - -import ( - "database/sql/driver" - "errors" - "fmt" - "reflect" - "time" -) - -// Packets documentation: -// http://forge.mysql.com/wiki/MySQL_Internals_ClientServer_Protocol - -// Read packet to buffer 'data' -func (mc *mysqlConn) readPacket() ([]byte, error) { - // Packet Length - pktLen, e := mc.readNumber(3) - if e != nil { - return nil, e - } - - if int(pktLen) == 0 { - return nil, e - } - - // Packet Number - pktSeq, e := mc.readNumber(1) - if e != nil { - return nil, e - } - - // Check Packet Sync - if uint8(pktSeq) != mc.sequence { - e = errors.New("Commands out of sync; you can't run this command now") - return nil, e - } - mc.sequence++ - - // Read rest of packet - data := make([]byte, pktLen) - var n, add int - for e == nil && n < int(pktLen) { - add, e = mc.bufReader.Read(data[n:]) - n += add - } - if e != nil || n < int(pktLen) { - if e == nil { - e = fmt.Errorf("Length of read data (%d) does not match body length (%d)", n, pktLen) - } - errLog.Print(`packets:58 `, e) - return nil, driver.ErrBadConn - } - return data, e -} - -// Read n bytes long number num -func (mc *mysqlConn) readNumber(nr uint8) (uint64, error) { - // Read bytes into array - buf := make([]byte, nr) - var n, add int - var e error - for e == nil && n < int(nr) { - add, e = mc.bufReader.Read(buf[n:]) - n += add - } - if e != nil || n < int(nr) { - if e == nil { - e = fmt.Errorf("Length of read data (%d) does not match header length (%d)", n, nr) - } - errLog.Print(`packets:78 `, e) - return 0, driver.ErrBadConn - } - - // Convert to uint64 - var num uint64 = 0 - for i := uint8(0); i < nr; i++ { - num |= uint64(buf[i]) << (i * 8) - } - return num, e -} - -func (mc *mysqlConn) writePacket(data *[]byte) error { - // Set time BEFORE to avoid possible collisions - if mc.server.keepalive > 0 { - mc.lastCmdTime = time.Now() - } - - // Write packet - n, e := mc.netConn.Write(*data) - if e != nil || n != len(*data) { - if e == nil { - e = errors.New("Length of send data does not match packet length") - } - errLog.Print(`packets:102 `, e) - return driver.ErrBadConn - } - - mc.sequence++ - return nil -} - -/****************************************************************************** -* Initialisation Process * -******************************************************************************/ - -/* Handshake Initialization Packet - Bytes Name - ----- ---- - 1 protocol_version - n (Null-Terminated String) server_version - 4 thread_id - 8 scramble_buff - 1 (filler) always 0x00 - 2 server_capabilities - 1 server_language - 2 server_status - 2 server capabilities (two upper bytes) - 1 length of the scramble -10 (filler) always 0 - n rest of the plugin provided data (at least 12 bytes) - 1 \0 byte, terminating the second part of a scramble -*/ -func (mc *mysqlConn) readInitPacket() (e error) { - data, e := mc.readPacket() - if e != nil { - return - } - - mc.server = new(serverSettings) - - // Position - pos := 0 - - // Protocol version [8 bit uint] - mc.server.protocol = data[pos] - if mc.server.protocol < MIN_PROTOCOL_VERSION { - e = fmt.Errorf( - "Unsupported MySQL Protocol Version %d. Protocol Version %d or higher is required", - mc.server.protocol, - MIN_PROTOCOL_VERSION) - } - pos++ - - // Server version [null terminated string] - slice, err := readSlice(data[pos:], 0x00) - if err != nil { - return - } - mc.server.version = string(slice) - pos += len(slice) + 1 - - // Thread id [32 bit uint] - mc.server.threadID = bytesToUint32(data[pos : pos+4]) - pos += 4 - - // First part of scramble buffer [8 bytes] - mc.server.scrambleBuff = make([]byte, 8) - mc.server.scrambleBuff = data[pos : pos+8] - pos += 9 - - // Server capabilities [16 bit uint] - mc.server.flags = ClientFlag(bytesToUint16(data[pos : pos+2])) - if mc.server.flags&CLIENT_PROTOCOL_41 == 0 { - e = errors.New("MySQL-Server does not support required Protocol 41+") - } - pos += 2 - - // Server language [8 bit uint] - mc.server.charset = data[pos] - pos++ - - // Server status [16 bit uint] - pos += 15 - - mc.server.scrambleBuff = append(mc.server.scrambleBuff, data[pos:pos+12]...) - - return -} - -/* Client Authentication Packet -Bytes Name ------ ---- -4 client_flags -4 max_packet_size -1 charset_number -23 (filler) always 0x00... -n (Null-Terminated String) user -n (Length Coded Binary) scramble_buff (1 + x bytes) -n (Null-Terminated String) databasename (optional) -*/ -func (mc *mysqlConn) writeAuthPacket() (e error) { - // Adjust client flags based on server support - clientFlags := uint32(CLIENT_MULTI_STATEMENTS | - // CLIENT_MULTI_RESULTS | - CLIENT_PROTOCOL_41 | - CLIENT_SECURE_CONN | - CLIENT_LONG_PASSWORD | - CLIENT_TRANSACTIONS) - if mc.server.flags&CLIENT_LONG_FLAG > 0 { - clientFlags |= uint32(CLIENT_LONG_FLAG) - } - // To specify a db name - if len(mc.cfg.dbname) > 0 { - clientFlags |= uint32(CLIENT_CONNECT_WITH_DB) - } - - // User Password - scrambleBuff := scramblePassword(mc.server.scrambleBuff, []byte(mc.cfg.passwd)) - - // Calculate packet length and make buffer with that size - pktLen := 4 + 4 + 1 + 23 + len(mc.cfg.user) + 1 + 1 + len(scrambleBuff) + len(mc.cfg.dbname) + 1 - data := make([]byte, 0, pktLen+4) - - // Add the packet header - data = append(data, uint24ToBytes(uint32(pktLen))...) - data = append(data, mc.sequence) - - // ClientFlags - data = append(data, uint32ToBytes(clientFlags)...) - - // MaxPacketSize - data = append(data, uint32ToBytes(MAX_PACKET_SIZE)...) - - // Charset - data = append(data, mc.server.charset) - - // Filler - data = append(data, make([]byte, 23)...) - - // User - if len(mc.cfg.user) > 0 { - data = append(data, []byte(mc.cfg.user)...) - } - - // Null-Terminator - data = append(data, 0x0) - - // ScrambleBuffer - data = append(data, byte(len(scrambleBuff))) - if len(scrambleBuff) > 0 { - data = append(data, scrambleBuff...) - } - - // Databasename - if len(mc.cfg.dbname) > 0 { - data = append(data, []byte(mc.cfg.dbname)...) - // Null-Terminator - data = append(data, 0x0) - } - - // Send Auth packet - return mc.writePacket(&data) -} - -/****************************************************************************** -* Command Packets * -******************************************************************************/ - -/* Command Packet -Bytes Name ------ ---- -1 command -n arg -*/ -func (mc *mysqlConn) writeCommandPacket(command commandType, args ...interface{}) (e error) { - // Reset Packet Sequence - mc.sequence = 0 - - var arg []byte - - switch command { - - // Commands without args - case COM_QUIT, COM_PING: - if len(args) > 0 { - return fmt.Errorf("Too much arguments (Got: %d Has: 0)", len(args)) - } - arg = []byte{} - - // Commands with 1 arg unterminated string - case COM_QUERY, COM_STMT_PREPARE: - if len(args) != 1 { - return fmt.Errorf("Invalid arguments count (Got: %d Has: 1)", len(args)) - } - arg = []byte(args[0].(string)) - - // Commands with 1 arg 32 bit uint - case COM_STMT_CLOSE: - if len(args) != 1 { - return fmt.Errorf("Invalid arguments count (Got: %d Has: 1)", len(args)) - } - arg = uint32ToBytes(args[0].(uint32)) - - default: - return fmt.Errorf("Unknown command: %d", command) - } - - pktLen := 1 + len(arg) - data := make([]byte, 0, pktLen+4) - - // Add the packet header - data = append(data, uint24ToBytes(uint32(pktLen))...) - data = append(data, mc.sequence) - - // Add command byte - data = append(data, byte(command)) - - // Add arg - data = append(data, arg...) - - // Send CMD packet - return mc.writePacket(&data) -} - -/****************************************************************************** -* Result Packets * -******************************************************************************/ - -// Returns error if Packet is not an 'Result OK'-Packet -func (mc *mysqlConn) readResultOK() (e error) { - data, e := mc.readPacket() - if e != nil { - return - } - - switch data[0] { - // OK - case 0: - return mc.handleOkPacket(data) - // ERROR - case 255: - return mc.handleErrorPacket(data) - default: - e = errors.New("Invalid Result Packet-Type") - return - } - - return -} - -/* Error Packet -Bytes Name ------ ---- -1 field_count, always = 0xff -2 errno -1 (sqlstate marker), always '#' -5 sqlstate (5 characters) -n message -*/ -func (mc *mysqlConn) handleErrorPacket(data []byte) (e error) { - if data[0] != 255 { - e = errors.New("Wrong Packet-Type: Not an Error-Packet") - return - } - - pos := 1 - - // Error Number [16 bit uint] - errno := bytesToUint16(data[pos : pos+2]) - pos += 2 - - // SQL State [# + 5bytes string] - //sqlstate := string(data[pos : pos+6]) - pos += 6 - - // Error Message [string] - message := string(data[pos:]) - - e = fmt.Errorf("Error %d: %s", errno, message) - return -} - -/* Ok Packet -Bytes Name ------ ---- -1 (Length Coded Binary) field_count, always = 0 -1-9 (Length Coded Binary) affected_rows -1-9 (Length Coded Binary) insert_id -2 server_status -2 warning_count -n (until end of packet) message -*/ -func (mc *mysqlConn) handleOkPacket(data []byte) (e error) { - if data[0] != 0 { - e = errors.New("Wrong Packet-Type: Not an OK-Packet") - return - } - - // Position - pos := 1 - - // Affected rows [Length Coded Binary] - affectedRows, n, e := bytesToLengthCodedBinary(data[pos:]) - if e != nil { - return - } - pos += n - - // Insert id [Length Coded Binary] - insertID, n, e := bytesToLengthCodedBinary(data[pos:]) - if e != nil { - return - } - - // Skip remaining data - - mc.affectedRows = affectedRows - mc.insertId = insertID - - return -} - -/* Result Set Header Packet - Bytes Name - ----- ---- - 1-9 (Length-Coded-Binary) field_count - 1-9 (Length-Coded-Binary) extra - -The order of packets for a result set is: - (Result Set Header Packet) the number of columns - (Field Packets) column descriptors - (EOF Packet) marker: end of Field Packets - (Row Data Packets) row contents - (EOF Packet) marker: end of Data Packets -*/ -func (mc *mysqlConn) readResultSetHeaderPacket() (fieldCount int, e error) { - data, e := mc.readPacket() - if e != nil { - errLog.Print(`packets:437 `, e) - e = driver.ErrBadConn - return - } - - if data[0] == 255 { - e = mc.handleErrorPacket(data) - return - } else if data[0] == 0 { - e = mc.handleOkPacket(data) - return - } - - num, n, e := bytesToLengthCodedBinary(data) - if e != nil || (n-len(data)) != 0 { - e = errors.New("Malformed Packet") - return - } - - fieldCount = int(num) - return -} - -// Read Packets as Field Packets until EOF-Packet or an Error appears -func (mc *mysqlConn) readColumns(n int) (columns []mysqlField, e error) { - var data []byte - - for { - data, e = mc.readPacket() - if e != nil { - return - } - - // EOF Packet - if data[0] == 254 && len(data) == 5 { - if len(columns) != n { - e = fmt.Errorf("ColumnsCount mismatch n:%d len:%d", n, len(columns)) - } - return - } - - var pos, n int - var name []byte - //var catalog, database, table, orgTable, name, orgName []byte - //var defaultVal uint64 - - // Catalog - //catalog, n, _, e = readLengthCodedBinary(data) - n, e = readAndDropLengthCodedBinary(data) - if e != nil { - return - } - pos += n - - // Database [len coded string] - //database, n, _, e = readLengthCodedBinary(data[pos:]) - n, e = readAndDropLengthCodedBinary(data[pos:]) - if e != nil { - return - } - pos += n - - // Table [len coded string] - //table, n, _, e = readLengthCodedBinary(data[pos:]) - n, e = readAndDropLengthCodedBinary(data[pos:]) - if e != nil { - return - } - pos += n - - // Original table [len coded string] - //orgTable, n, _, e = readLengthCodedBinary(data[pos:]) - n, e = readAndDropLengthCodedBinary(data[pos:]) - if e != nil { - return - } - pos += n - - // Name [len coded string] - name, n, _, e = readLengthCodedBinary(data[pos:]) - if e != nil { - return - } - pos += n - - // Original name [len coded string] - //orgName, n, _, e = readLengthCodedBinary(data[pos:]) - n, e = readAndDropLengthCodedBinary(data[pos:]) - if e != nil { - return - } - pos += n - - // Filler - pos++ - - // Charset [16 bit uint] - //charsetNumber := bytesToUint16(data[pos : pos+2]) - pos += 2 - - // Length [32 bit uint] - //length := bytesToUint32(data[pos : pos+4]) - pos += 4 - - // Field type [byte] - fieldType := FieldType(data[pos]) - pos++ - - // Flags [16 bit uint] - flags := FieldFlag(bytesToUint16(data[pos : pos+2])) - //pos += 2 - - // Decimals [8 bit uint] - //decimals := data[pos] - //pos++ - - // Default value [len coded binary] - //if pos < len(data) { - // defaultVal, _, e = bytesToLengthCodedBinary(data[pos:]) - //} - - columns = append(columns, mysqlField{name: string(name), fieldType: fieldType, flags: flags}) - } - - return -} - -// Read Packets as Field Packets until EOF-Packet or an Error appears -func (mc *mysqlConn) readRows(columnsCount int) (rows []*[][]byte, e error) { - var data []byte - var i, pos, n int - var isNull bool - - for { - data, e = mc.readPacket() - if e != nil { - return - } - - // EOF Packet - if data[0] == 254 && len(data) == 5 { - return - } - - // RowSet Packet - row := make([][]byte, columnsCount) - pos = 0 - for i = 0; i < columnsCount; i++ { - // Read bytes and convert to string - row[i], n, isNull, e = readLengthCodedBinary(data[pos:]) - if e != nil { - return - } - - // Append nil if field is NULL - if isNull { - row[i] = nil - } - pos += n - } - rows = append(rows, &row) - } - - mc.affectedRows = uint64(len(rows)) - return -} - -// Reads Packets Packets until EOF-Packet or an Error appears. Returns count of Packets read -func (mc *mysqlConn) readUntilEOF() (count uint64, e error) { - var data []byte - - for { - data, e = mc.readPacket() - if e != nil { - return - } - - // EOF Packet - if data[0] == 254 && len(data) == 5 { - return - } - - count++ - } - return -} - -/****************************************************************************** -* Prepared Statements * -******************************************************************************/ - -/* Prepare Result Packets - Type Of Result Packet Hexadecimal Value Of First Byte (field_count) - --------------------- --------------------------------------------- - - Prepare OK Packet 00 - Error Packet ff - -Prepare OK Packet - Bytes Name - ----- ---- - 1 0 - marker for OK packet - 4 statement_handler_id - 2 number of columns in result set - 2 number of parameters in query - 1 filler (always 0) - 2 warning count - - It is made up of: - - a PREPARE_OK packet - if "number of parameters" > 0 - (field packets) as in a Result Set Header Packet - (EOF packet) - if "number of columns" > 0 - (field packets) as in a Result Set Header Packet - (EOF packet) - -*/ -func (stmt mysqlStmt) readPrepareResultPacket() (columnCount uint16, e error) { - data, e := stmt.mc.readPacket() - if e != nil { - return - } - - // Position - pos := 0 - - if data[pos] != 0 { - e = stmt.mc.handleErrorPacket(data) - return - } - pos++ - - stmt.id = bytesToUint32(data[pos : pos+4]) - pos += 4 - - // Column count [16 bit uint] - columnCount = bytesToUint16(data[pos : pos+2]) - pos += 2 - - // Param count [16 bit uint] - stmt.paramCount = int(bytesToUint16(data[pos : pos+2])) - pos += 2 - - // Warning count [16 bit uint] - // bytesToUint16(data[pos : pos+2]) - - return -} - -/* Command Packet -Bytes Name ------ ---- -1 code -4 statement_id -1 flags -4 iteration_count - if param_count > 0: -(param_count+7)/8 null_bit_map -1 new_parameter_bound_flag - if new_params_bound == 1: -n*2 type of parameters -n values for the parameters -*/ -func (stmt mysqlStmt) buildExecutePacket(args *[]driver.Value) (e error) { - argsLen := len(*args) - if argsLen < stmt.paramCount { - return fmt.Errorf( - "Not enough Arguments to call STMT_EXEC (Got: %d Has: %d", - argsLen, - stmt.paramCount) - } - - // Reset packet-sequence - stmt.mc.sequence = 0 - - pktLen := 1 + 4 + 1 + 4 + (stmt.paramCount+7)/8 + 1 + argsLen*2 - paramValues := make([][]byte, 0, argsLen) - paramTypes := make([]byte, 0, argsLen*2) - bitMask := uint64(0) - var i, valLen int - var pv reflect.Value - for i = 0; i < stmt.paramCount; i++ { - // build nullBitMap - if (*args)[i] == nil { - bitMask += 1 << uint(i) - } - - // cache types and values - switch (*args)[i].(type) { - case nil: - paramTypes = append(paramTypes, []byte{ - byte(FIELD_TYPE_NULL), - 0x0}...) - continue - - case []byte: - paramTypes = append(paramTypes, []byte{byte(FIELD_TYPE_STRING), 0x0}...) - val := (*args)[i].([]byte) - valLen = len(val) - lcb := lengthCodedBinaryToBytes(uint64(valLen)) - pktLen += len(lcb) + valLen - paramValues = append(paramValues, lcb) - paramValues = append(paramValues, val) - continue - - case time.Time: - // Format to string for time+date Fields - // Data is packed in case reflect.String below - (*args)[i] = (*args)[i].(time.Time).Format(TIME_FORMAT) - } - - pv = reflect.ValueOf((*args)[i]) - switch pv.Kind() { - case reflect.Int64: - paramTypes = append(paramTypes, []byte{byte(FIELD_TYPE_LONGLONG), 0x0}...) - val := int64ToBytes(pv.Int()) - pktLen += len(val) - paramValues = append(paramValues, val) - continue - - case reflect.Float64: - paramTypes = append(paramTypes, []byte{byte(FIELD_TYPE_DOUBLE), 0x0}...) - val := float64ToBytes(pv.Float()) - pktLen += len(val) - paramValues = append(paramValues, val) - continue - - case reflect.Bool: - paramTypes = append(paramTypes, []byte{byte(FIELD_TYPE_TINY), 0x0}...) - val := pv.Bool() - pktLen++ - if val { - paramValues = append(paramValues, []byte{byte(1)}) - } else { - paramValues = append(paramValues, []byte{byte(0)}) - } - continue - - case reflect.String: - paramTypes = append(paramTypes, []byte{byte(FIELD_TYPE_STRING), 0x0}...) - val := []byte(pv.String()) - valLen = len(val) - lcb := lengthCodedBinaryToBytes(uint64(valLen)) - pktLen += valLen + len(lcb) - paramValues = append(paramValues, lcb) - paramValues = append(paramValues, val) - continue - - default: - return fmt.Errorf("Invalid Value: %s", pv.Kind().String()) - } - } - - data := make([]byte, 0, pktLen+4) - - // Add the packet header - data = append(data, uint24ToBytes(uint32(pktLen))...) - data = append(data, stmt.mc.sequence) - - // code [1 byte] - data = append(data, byte(COM_STMT_EXECUTE)) - - // statement_id [4 bytes] - data = append(data, uint32ToBytes(stmt.id)...) - - // flags (0: CURSOR_TYPE_NO_CURSOR) [1 byte] - data = append(data, byte(0)) - - // iteration_count [4 bytes] - data = append(data, uint32ToBytes(1)...) - - // append nullBitMap [(param_count+7)/8 bytes] - if stmt.paramCount > 0 { - // Convert bitMask to bytes - nullBitMap := make([]byte, (stmt.paramCount+7)/8) - for i = 0; i < len(nullBitMap); i++ { - nullBitMap[i] = byte(bitMask >> uint(i*8)) - } - - data = append(data, nullBitMap...) - } - - // newParameterBoundFlag 1 [1 byte] - data = append(data, byte(1)) - - // type of parameters [n*2 byte] - data = append(data, paramTypes...) - - // values for the parameters [n byte] - for _, paramValue := range paramValues { - data = append(data, paramValue...) - } - - return stmt.mc.writePacket(&data) -} - -func (mc *mysqlConn) readBinaryRows(rc *rowsContent) (e error) { - var data, nullBitMap []byte - var i, pos, n int - var unsigned, isNull bool - columnsCount := len(rc.columns) - - for { - data, e = mc.readPacket() - if e != nil { - return - } - - pos = 0 - - // EOF Packet - if data[pos] == 254 && len(data) == 5 { - return - } - - pos++ - - // BinaryRowSet Packet - row := make([][]byte, columnsCount) - - nullBitMap = data[pos : pos+(columnsCount+7+2)/8] - pos += (columnsCount + 7 + 2) / 8 - - for i = 0; i < columnsCount; i++ { - // Field is NULL - if (nullBitMap[(i+2)/8] >> uint((i+2)%8) & 1) == 1 { - row[i] = nil - continue - } - - unsigned = rc.columns[i].flags&FLAG_UNSIGNED != 0 - - // Convert to byte-coded string - switch rc.columns[i].fieldType { - case FIELD_TYPE_NULL: - row[i] = nil - - // Numeric Typs - case FIELD_TYPE_TINY: - if unsigned { - row[i] = uintToByteStr(uint64(byteToUint8(data[pos]))) - } else { - row[i] = intToByteStr(int64(int8(byteToUint8(data[pos])))) - } - pos++ - - case FIELD_TYPE_SHORT, FIELD_TYPE_YEAR: - if unsigned { - row[i] = uintToByteStr(uint64(bytesToUint16(data[pos : pos+2]))) - } else { - row[i] = intToByteStr(int64(int16(bytesToUint16(data[pos : pos+2])))) - } - pos += 2 - - case FIELD_TYPE_INT24, FIELD_TYPE_LONG: - if unsigned { - row[i] = uintToByteStr(uint64(bytesToUint32(data[pos : pos+4]))) - } else { - row[i] = intToByteStr(int64(int32(bytesToUint32(data[pos : pos+4])))) - } - pos += 4 - - case FIELD_TYPE_LONGLONG: - if unsigned { - row[i] = uintToByteStr(bytesToUint64(data[pos : pos+8])) - } else { - row[i] = intToByteStr(int64(bytesToUint64(data[pos : pos+8]))) - } - pos += 8 - - case FIELD_TYPE_FLOAT: - row[i] = float32ToByteStr(bytesToFloat32(data[pos : pos+4])) - pos += 4 - - case FIELD_TYPE_DOUBLE: - row[i] = float64ToByteStr(bytesToFloat64(data[pos : pos+8])) - pos += 8 - - case FIELD_TYPE_DECIMAL, FIELD_TYPE_NEWDECIMAL: - row[i], n, isNull, e = readLengthCodedBinary(data[pos:]) - if e != nil { - return - } - - if isNull && rc.columns[i].flags&FLAG_NOT_NULL == 0 { - row[i] = nil - } - pos += n - - // Length coded Binary Strings - case FIELD_TYPE_VARCHAR, FIELD_TYPE_BIT, FIELD_TYPE_ENUM, - FIELD_TYPE_SET, FIELD_TYPE_TINY_BLOB, FIELD_TYPE_MEDIUM_BLOB, - FIELD_TYPE_LONG_BLOB, FIELD_TYPE_BLOB, FIELD_TYPE_VAR_STRING, - FIELD_TYPE_STRING, FIELD_TYPE_GEOMETRY: - row[i], n, isNull, e = readLengthCodedBinary(data[pos:]) - if e != nil { - return - } - - if isNull && rc.columns[i].flags&FLAG_NOT_NULL == 0 { - row[i] = nil - } - pos += n - - // Date YYYY-MM-DD - case FIELD_TYPE_DATE, FIELD_TYPE_NEWDATE: - var num uint64 - num, n, e = bytesToLengthCodedBinary(data[pos:]) - if e != nil { - return - } - pos += n - - if num == 0 { - row[i] = []byte("0000-00-00") - } else { - row[i] = []byte(fmt.Sprintf("%04d-%02d-%02d", - bytesToUint16(data[pos:pos+2]), - data[pos+2], - data[pos+3])) - } - pos += int(num) - - // Time HH:MM:SS - case FIELD_TYPE_TIME: - var num uint64 - num, n, e = bytesToLengthCodedBinary(data[pos:]) - if e != nil { - return - } - - if num == 0 { - row[i] = []byte("00:00:00") - } else { - row[i] = []byte(fmt.Sprintf("%02d:%02d:%02d", - data[pos+6], - data[pos+7], - data[pos+8])) - } - pos += n + int(num) - - // Timestamp YYYY-MM-DD HH:MM:SS - case FIELD_TYPE_TIMESTAMP, FIELD_TYPE_DATETIME: - var num uint64 - num, n, e = bytesToLengthCodedBinary(data[pos:]) - if e != nil { - return - } - pos += n - - if num == 0 { - row[i] = []byte("0000-00-00 00:00:00") - } else { - row[i] = []byte(fmt.Sprintf("%04d-%02d-%02d %02d:%02d:%02d", - bytesToUint16(data[pos:pos+2]), - data[pos+2], - data[pos+3], - data[pos+4], - data[pos+5], - data[pos+6])) - } - pos += int(num) - - // Please report if this happens! - default: - return fmt.Errorf("Unknown FieldType %d", rc.columns[i].fieldType) - } - } - rc.rows = append(rc.rows, &row) - } - - mc.affectedRows = uint64(len(rc.rows)) - return -} diff --git a/src/code.google.com/p/go-mysql-driver/mysql/result.go b/src/code.google.com/p/go-mysql-driver/mysql/result.go deleted file mode 100644 index 350bd64..0000000 --- a/src/code.google.com/p/go-mysql-driver/mysql/result.go +++ /dev/null @@ -1,22 +0,0 @@ -// Go MySQL Driver - A MySQL-Driver for Go's database/sql package -// -// Copyright 2012 Julien Schmidt. All rights reserved. -// http://www.julienschmidt.com -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this file, -// You can obtain one at http://mozilla.org/MPL/2.0/. -package mysql - -type mysqlResult struct { - affectedRows int64 - insertId int64 -} - -func (res mysqlResult) LastInsertId() (int64, error) { - return res.insertId, nil -} - -func (res mysqlResult) RowsAffected() (int64, error) { - return res.affectedRows, nil -} diff --git a/src/code.google.com/p/go-mysql-driver/mysql/rows.go b/src/code.google.com/p/go-mysql-driver/mysql/rows.go deleted file mode 100644 index e3de282..0000000 --- a/src/code.google.com/p/go-mysql-driver/mysql/rows.go +++ /dev/null @@ -1,58 +0,0 @@ -// Go MySQL Driver - A MySQL-Driver for Go's database/sql package -// -// Copyright 2012 Julien Schmidt. All rights reserved. -// http://www.julienschmidt.com -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this file, -// You can obtain one at http://mozilla.org/MPL/2.0/. -package mysql - -import ( - "database/sql/driver" - "io" -) - -type mysqlField struct { - name string - fieldType FieldType - flags FieldFlag -} - -type rowsContent struct { - columns []mysqlField - rows []*[][]byte -} - -type mysqlRows struct { - content *rowsContent -} - -func (rows mysqlRows) Columns() (columns []string) { - columns = make([]string, len(rows.content.columns)) - for i := 0; i < cap(columns); i++ { - columns[i] = rows.content.columns[i].name - } - return -} - -func (rows mysqlRows) Close() error { - rows.content = nil - return nil -} - -// Next returns []driver.Value filled with either nil values for NULL entries -// or []byte's for all other entries. Type conversion is done on rows.scan(), -// when the dest. type is know, which makes type conversion easier and avoids -// unnecessary conversions. -func (rows mysqlRows) Next(dest []driver.Value) error { - if len(rows.content.rows) > 0 { - for i := 0; i < cap(dest); i++ { - dest[i] = (*rows.content.rows[0])[i] - } - rows.content.rows = rows.content.rows[1:] - } else { - return io.EOF - } - return nil -} diff --git a/src/code.google.com/p/go-mysql-driver/mysql/statement.go b/src/code.google.com/p/go-mysql-driver/mysql/statement.go deleted file mode 100644 index 9ada679..0000000 --- a/src/code.google.com/p/go-mysql-driver/mysql/statement.go +++ /dev/null @@ -1,128 +0,0 @@ -// Go MySQL Driver - A MySQL-Driver for Go's database/sql package -// -// Copyright 2012 Julien Schmidt. All rights reserved. -// http://www.julienschmidt.com -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this file, -// You can obtain one at http://mozilla.org/MPL/2.0/. -package mysql - -import ( - "database/sql/driver" - "errors" -) - -type stmtContent struct { - mc *mysqlConn - id uint32 - paramCount int - params []mysqlField -} - -type mysqlStmt struct { - *stmtContent -} - -func (stmt mysqlStmt) Close() error { - e := stmt.mc.writeCommandPacket(COM_STMT_CLOSE, stmt.id) - stmt.mc = nil - return e -} - -func (stmt mysqlStmt) NumInput() int { - return stmt.paramCount -} - -func (stmt mysqlStmt) Exec(args []driver.Value) (driver.Result, error) { - if stmt.mc == nil { - return nil, errors.New(`Invalid Statement`) - } - stmt.mc.affectedRows = 0 - stmt.mc.insertId = 0 - - // Send command - e := stmt.buildExecutePacket(&args) - if e != nil { - return nil, e - } - - // Read Result - var resLen int - resLen, e = stmt.mc.readResultSetHeaderPacket() - if e != nil { - return nil, e - } - - if resLen > 0 { - // Columns - _, e = stmt.mc.readUntilEOF() - if e != nil { - return nil, e - } - - // Rows - stmt.mc.affectedRows, e = stmt.mc.readUntilEOF() - if e != nil { - return nil, e - } - } - if e != nil { - return nil, e - } - - if stmt.mc.affectedRows == 0 { - return driver.ResultNoRows, nil - } - - return mysqlResult{ - affectedRows: int64(stmt.mc.affectedRows), - insertId: int64(stmt.mc.insertId)}, - nil -} - -func (stmt mysqlStmt) Query(args []driver.Value) (dr driver.Rows, e error) { - if stmt.mc == nil { - return nil, errors.New(`Invalid Statement`) - } - - // Send command - e = stmt.buildExecutePacket(&args) - if e != nil { - return nil, e - } - - // Get Result - var resLen int - rows := mysqlRows{new(rowsContent)} - resLen, e = stmt.mc.readResultSetHeaderPacket() - if e != nil { - return nil, e - } - - if resLen > 0 { - // Columns - rows.content.columns, e = stmt.mc.readColumns(resLen) - if e != nil { - return - } - - // Rows - e = stmt.mc.readBinaryRows(rows.content) - if e != nil { - return - } - } - - dr = rows - return -} - -// ColumnConverter returns a ValueConverter for the provided -// column index. If the type of a specific column isn't known -// or shouldn't be handled specially, DefaultValueConverter -// can be returned. -//func (stmt mysqlStmt) ColumnConverter(idx int) driver.ValueConverter { -// debug(fmt.Sprintf("ColumnConverter(%d)", idx)) -// return driver.DefaultParameterConverter -//} diff --git a/src/code.google.com/p/go-mysql-driver/mysql/transaction.go b/src/code.google.com/p/go-mysql-driver/mysql/transaction.go deleted file mode 100644 index 122f570..0000000 --- a/src/code.google.com/p/go-mysql-driver/mysql/transaction.go +++ /dev/null @@ -1,25 +0,0 @@ -// Go MySQL Driver - A MySQL-Driver for Go's database/sql package -// -// Copyright 2012 Julien Schmidt. All rights reserved. -// http://www.julienschmidt.com -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this file, -// You can obtain one at http://mozilla.org/MPL/2.0/. -package mysql - -type mysqlTx struct { - mc *mysqlConn -} - -func (tx *mysqlTx) Commit() (e error) { - e = tx.mc.exec("COMMIT") - tx.mc = nil - return -} - -func (tx *mysqlTx) Rollback() (e error) { - e = tx.mc.exec("ROLLBACK") - tx.mc = nil - return -} diff --git a/src/code.google.com/p/go-mysql-driver/mysql/utils.go b/src/code.google.com/p/go-mysql-driver/mysql/utils.go deleted file mode 100644 index ce09ade..0000000 --- a/src/code.google.com/p/go-mysql-driver/mysql/utils.go +++ /dev/null @@ -1,316 +0,0 @@ -// Go MySQL Driver - A MySQL-Driver for Go's database/sql package -// -// Copyright 2012 Julien Schmidt. All rights reserved. -// http://www.julienschmidt.com -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this file, -// You can obtain one at http://mozilla.org/MPL/2.0/. -package mysql - -import ( - "bytes" - "crypto/sha1" - "io" - "log" - "math" - "os" - "regexp" - "strconv" - "strings" -) - -// Logger -var ( - errLog *log.Logger - dbgLog *log.Logger -) - -func init() { - errLog = log.New(os.Stderr, "[MySQL] ", log.LstdFlags) - dbgLog = log.New(os.Stdout, "[MySQL] ", log.LstdFlags) - - dsnPattern = regexp.MustCompile( - `^(?:(?P.*?)(?::(?P.*))?@)?` + // [user[:password]@] - `(?:(?P[^\(]*)(?:\((?P[^\)]*)\))?)?` + // [net[(addr)]] - `\/(?P.*?)` + // /dbname - `(?:\?(?P[^\?]*))?$`) // [?param1=value1¶mN=valueN] -} - -// Data Source Name Parser -var dsnPattern *regexp.Regexp - -func parseDSN(dsn string) *config { - cfg := new(config) - cfg.params = make(map[string]string) - - matches := dsnPattern.FindStringSubmatch(dsn) - names := dsnPattern.SubexpNames() - - for i, match := range matches { - switch names[i] { - case "user": - cfg.user = match - case "passwd": - cfg.passwd = match - case "net": - cfg.net = match - case "addr": - cfg.addr = match - case "dbname": - cfg.dbname = match - case "params": - for _, v := range strings.Split(match, "&") { - param := strings.SplitN(v, "=", 2) - if len(param) != 2 { - continue - } - cfg.params[param[0]] = param[1] - } - } - } - - // Set default network if empty - if cfg.net == "" { - cfg.net = "tcp" - } - - // Set default adress if empty - if cfg.addr == "" { - cfg.addr = "127.0.0.1:3306" - } - - return cfg -} - -// Encrypt password using 4.1+ method -// http://forge.mysql.com/wiki/MySQL_Internals_ClientServer_Protocol#4.1_and_later -func scramblePassword(scramble, password []byte) (result []byte) { - if len(password) == 0 { - return - } - - // stage1Hash = SHA1(password) - crypt := sha1.New() - crypt.Write(password) - stage1Hash := crypt.Sum(nil) - - // scrambleHash = SHA1(scramble + SHA1(stage1Hash)) - // inner Hash - crypt.Reset() - crypt.Write(stage1Hash) - scrambleHash := crypt.Sum(nil) - - // outer Hash - crypt.Reset() - crypt.Write(scramble) - crypt.Write(scrambleHash) - scrambleHash = crypt.Sum(nil) - - // token = scrambleHash XOR stage1Hash - result = make([]byte, 20) - for i := range result { - result[i] = scrambleHash[i] ^ stage1Hash[i] - } - return -} - -/****************************************************************************** -* Read data-types from bytes * -******************************************************************************/ - -// Read a slice from the data slice -func readSlice(data []byte, delim byte) (slice []byte, e error) { - pos := bytes.IndexByte(data, delim) - if pos > -1 { - slice = data[:pos] - } else { - slice = data - e = io.EOF - } - return -} - -func readLengthCodedBinary(data []byte) (b []byte, n int, isNull bool, e error) { - // Get length - num, n, e := bytesToLengthCodedBinary(data) - if e != nil { - return - } - - // Check data length - if len(data) < n+int(num) { - e = io.EOF - return - } - - // Check if null - if data[0] == 251 { - isNull = true - } else { - isNull = false - } - - // Get bytes - b = data[n : n+int(num)] - n += int(num) - return -} - -func readAndDropLengthCodedBinary(data []byte) (n int, e error) { - // Get length - num, n, e := bytesToLengthCodedBinary(data) - if e != nil { - return - } - - // Check data length - if len(data) < n+int(num) { - e = io.EOF - return - } - - n += int(num) - return -} - -/****************************************************************************** -* Convert from and to bytes * -******************************************************************************/ - -func byteToUint8(b byte) (n uint8) { - n |= uint8(b) - return -} - -func bytesToUint16(b []byte) (n uint16) { - n |= uint16(b[0]) - n |= uint16(b[1]) << 8 - return -} - -func uint24ToBytes(n uint32) (b []byte) { - b = make([]byte, 3) - for i := uint8(0); i < 3; i++ { - b[i] = byte(n >> (i * 8)) - } - return -} - -func bytesToUint32(b []byte) (n uint32) { - for i := uint8(0); i < 4; i++ { - n |= uint32(b[i]) << (i * 8) - } - return -} - -func uint32ToBytes(n uint32) (b []byte) { - b = make([]byte, 4) - for i := uint8(0); i < 4; i++ { - b[i] = byte(n >> (i * 8)) - } - return -} - -func bytesToUint64(b []byte) (n uint64) { - for i := uint8(0); i < 8; i++ { - n |= uint64(b[i]) << (i * 8) - } - return -} - -func uint64ToBytes(n uint64) (b []byte) { - b = make([]byte, 8) - for i := uint8(0); i < 8; i++ { - b[i] = byte(n >> (i * 8)) - } - return -} - -func int64ToBytes(n int64) []byte { - return uint64ToBytes(uint64(n)) -} - -func bytesToFloat32(b []byte) float32 { - return math.Float32frombits(bytesToUint32(b)) -} - -func bytesToFloat64(b []byte) float64 { - return math.Float64frombits(bytesToUint64(b)) -} - -func float64ToBytes(f float64) []byte { - return uint64ToBytes(math.Float64bits(f)) -} - -func bytesToLengthCodedBinary(b []byte) (length uint64, n int, e error) { - switch { - - // 0-250: value of first byte - case b[0] <= 250: - length = uint64(b[0]) - n = 1 - return - - // 251: NULL - case b[0] == 251: - length = 0 - n = 1 - return - - // 252: value of following 2 - case b[0] == 252: - n = 3 - - // 253: value of following 3 - case b[0] == 253: - n = 4 - - // 254: value of following 8 - case b[0] == 254: - n = 9 - } - - if len(b) < n { - e = io.EOF - return - } - - // get Length - tmp := make([]byte, 8) - copy(tmp, b[1:n]) - length = bytesToUint64(tmp) - return -} - -func lengthCodedBinaryToBytes(n uint64) (b []byte) { - switch { - - case n <= 250: - b = []byte{byte(n)} - - case n <= 0xffff: - b = []byte{0xfc, byte(n), byte(n >> 8)} - - case n <= 0xffffff: - b = []byte{0xfd, byte(n), byte(n >> 8), byte(n >> 16)} - } - return -} - -func intToByteStr(i int64) (b []byte) { - //tmp := make([]byte, 0) - return strconv.AppendInt(b, i, 10) -} - -func uintToByteStr(u uint64) (b []byte) { - return strconv.AppendUint(b, u, 10) -} - -func float32ToByteStr(f float32) (b []byte) { - return strconv.AppendFloat(b, float64(f), 'f', -1, 32) -} - -func float64ToByteStr(f float64) (b []byte) { - return strconv.AppendFloat(b, f, 'f', -1, 64) -} diff --git a/src/github.com/bmizerany/pq/.gitignore b/src/github.com/bmizerany/pq/.gitignore deleted file mode 100644 index 4640a57..0000000 --- a/src/github.com/bmizerany/pq/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -.db -*.test diff --git a/src/github.com/bmizerany/pq/README.md b/src/github.com/bmizerany/pq/README.md deleted file mode 100644 index 071eade..0000000 --- a/src/github.com/bmizerany/pq/README.md +++ /dev/null @@ -1,67 +0,0 @@ -# pq - A pure Go postgres driver for Go's database/sql package - -## Install - - go get github.com/bmizerany/pq - -## Use - - package main - - import ( - _ "github.com/bmizerany/pq" - "database/sql" - ) - - func main() { - db, err := sql.Open("postgres", "user=pqgotest dbname=pqgotest sslmode=verify-full") - // ... - } - -**Connection String Parameters** - -These are a subset of the libpq connection parameters. -See http://www.postgresql.org/docs/9.0/static/libpq-connect.html - -* `dbname` - The name of the database to connect to -* `user` - The user to sign in as -* `password` - The user's password -* `host` - The host to connect to. Values that start with `/` are for unix domain sockets. (default is `localhost`) -* `port` - The port to bind to. (default is `5432`) -* `sslmode` - Whether or not to use SSL (default is `require`, this is not the default for libpq) - Valid values are: - * `disable` - No SSL - * `require` - Always SSL (skip verification) - * `verify-full` - Always SSL (require verification) - -See http://tip.golang.org/pkg/database/sql to learn how to use with `pq` through the `database/sql` package. - -## Features - -* SSL -* Handles bad connections for `database/sql` -* Scan `time.Time` correctly (i.e. `timestamp[tz]`, `time[tz]`, `date`) -* Scan binary blobs correctly (i.e. `bytea`) -* pq.ParseURL for converting urls to connection strings for sql.Open. - -## Future / Things you can help with - -* Notifications: `LISTEN`/`NOTIFY` -* `hstore` sugar (i.e. handling hstore in `rows.Scan`) - -## Thank you (alphabetical) - -Some of these contributors are from the original library `bmizerany/pq.go` whose -code still exists in here. - -* Blake Gentry (bgentry) -* Brad Fitzpatrick (bradfitz) -* Daniel Farina (fdr) -* Everyone at The Go Team -* Federico Romero (federomero) -* Heroku (heroku) -* Keith Rarick (kr) -* Mike Lewis (mikelikespie) -* Ryan Smith (ryandotsmith) -* Samuel Stauffer (samuel) -* notedit (notedit) diff --git a/src/github.com/bmizerany/pq/buf.go b/src/github.com/bmizerany/pq/buf.go deleted file mode 100644 index c6be5d6..0000000 --- a/src/github.com/bmizerany/pq/buf.go +++ /dev/null @@ -1,74 +0,0 @@ -package pq - -import ( - "bytes" - "encoding/binary" -) - -type readBuf []byte - -func (b *readBuf) int32() (n int) { - n = int(binary.BigEndian.Uint32(*b)) - *b = (*b)[4:] - return -} - -func (b *readBuf) int16() (n int) { - n = int(binary.BigEndian.Uint16(*b)) - *b = (*b)[2:] - return -} - -var stringTerm = []byte{0} - -func (b *readBuf) string() string { - i := bytes.Index(*b, stringTerm) - if i < 0 { - errorf("invalid message format; expected string terminator") - } - s := (*b)[:i] - *b = (*b)[i+1:] - return string(s) -} - -func (b *readBuf) next(n int) (v []byte) { - v = (*b)[:n] - *b = (*b)[n:] - return -} - -func (b *readBuf) byte() byte { - return b.next(1)[0] -} - -type writeBuf []byte - -func newWriteBuf(c byte) *writeBuf { - b := make(writeBuf, 5) - b[0] = c - return &b -} - -func (b *writeBuf) int32(n int) { - x := make([]byte, 4) - binary.BigEndian.PutUint32(x, uint32(n)) - *b = append(*b, x...) -} - -func (b *writeBuf) int16(n int) { - x := make([]byte, 2) - binary.BigEndian.PutUint16(x, uint16(n)) - *b = append(*b, x...) -} - -func (b *writeBuf) string(s string) { - *b = append(*b, (s + "\000")...) -} - -func (b *writeBuf) byte(c byte) { - *b = append(*b, c) -} - -func (b *writeBuf) bytes(v []byte) { - *b = append(*b, v...) -} diff --git a/src/github.com/bmizerany/pq/conn.go b/src/github.com/bmizerany/pq/conn.go deleted file mode 100644 index 98267ac..0000000 --- a/src/github.com/bmizerany/pq/conn.go +++ /dev/null @@ -1,486 +0,0 @@ -package pq - -import ( - "crypto/md5" - "crypto/tls" - "database/sql" - "database/sql/driver" - "encoding/binary" - "errors" - "fmt" - "io" - "net" - "strconv" - "strings" -) - -var ( - ErrSSLNotSupported = errors.New("pq: SSL is not enabled on the server") - ErrNotSupported = errors.New("pq: this is postgres, a real database, this isn't a valid command") -) - -type drv struct{} - -func (d *drv) Open(name string) (driver.Conn, error) { - return Open(name) -} - -func init() { - sql.Register("postgres", &drv{}) -} - -type conn struct { - c net.Conn - namei int -} - -func Open(name string) (_ driver.Conn, err error) { - defer errRecover(&err) - - o := make(Values) - o.Set("host", "localhost") - o.Set("port", "5432") - parseOpts(name, o) - - c, err := net.Dial(network(o)) - if err != nil { - return nil, err - } - - cn := &conn{c: c} - cn.ssl(o) - cn.startup(o) - return cn, nil -} - -func network(o Values) (string, string) { - host := o.Get("host") - - if strings.HasPrefix(host, "/") { - return "unix", host - } - - return "tcp", host + ":" + o.Get("port") -} - -type Values map[string]string - -func (vs Values) Set(k, v string) { - vs[k] = v -} - -func (vs Values) Get(k string) (v string) { - v, _ = vs[k] - return -} - -func parseOpts(name string, o Values) { - if len(name) == 0 { - return - } - - ps := strings.Split(name, " ") - for _, p := range ps { - kv := strings.Split(p, "=") - if len(kv) < 2 { - errorf("invalid option: %q", p) - } - o.Set(kv[0], kv[1]) - } -} - -func (cn *conn) Begin() (driver.Tx, error) { - st, err := cn.Prepare("BEGIN") - if err != nil { - return nil, err - } - - _, err = st.Exec(nil) - return cn, err -} - -func (cn *conn) Commit() error { - st, err := cn.Prepare("COMMIT") - if err != nil { - return err - } - - _, err = st.Exec(nil) - return err -} - -func (cn *conn) Rollback() error { - st, err := cn.Prepare("ROLLBACK") - if err != nil { - return err - } - - _, err = st.Exec(nil) - return err -} - -func (cn *conn) gname() string { - cn.namei++ - return strconv.FormatInt(int64(cn.namei), 10) -} - -func (cn *conn) Prepare(q string) (_ driver.Stmt, err error) { - defer errRecover(&err) - - st := &stmt{cn: cn, name: cn.gname()} - - b := newWriteBuf('P') - b.string(st.name) - b.string(q) - b.int16(0) - cn.send(b) - - b = newWriteBuf('D') - b.byte('S') - b.string(st.name) - cn.send(b) - - cn.send(newWriteBuf('H')) - - t, r := cn.recv() - if t != '1' { - errorf("unexpected parse response: %q", t) - } - - t, r = cn.recv() - if t != 't' { - errorf("unexpected describe params response: %q", t) - } - st.nparams = int(r.int16()) - - t, r = cn.recv() - switch t { - case 'T': - n := r.int16() - st.cols = make([]string, n) - st.ooid = make([]int, n) - for i := range st.cols { - st.cols[i] = r.string() - r.next(6) - st.ooid[i] = r.int32() - r.next(8) - } - case 'n': - // no data - default: - errorf("unexpected describe rows response: %q", t) - } - - return st, nil -} - -func (cn *conn) Close() error { - return cn.c.Close() -} - -// Assumes len(*m) is > 5 -func (cn *conn) send(m *writeBuf) { - b := (*m)[1:] - binary.BigEndian.PutUint32(b, uint32(len(b))) - - if (*m)[0] == 0 { - *m = b - } - - _, err := cn.c.Write(*m) - if err != nil { - panic(err) - } -} - -func (cn *conn) recv() (t byte, r *readBuf) { - for { - t, r = cn.recv1() - switch t { - case 'E': - panic(parseError(r)) - case 'N': - // TODO(bmizerany): log notices? - default: - return - } - } - - panic("not reached") -} - -func (cn *conn) recv1() (byte, *readBuf) { - x := make([]byte, 5) - _, err := cn.c.Read(x) - if err != nil { - panic(err) - } - - b := readBuf(x[1:]) - y := make([]byte, b.int32()-4) - _, err = io.ReadFull(cn.c, y) - if err != nil { - panic(err) - } - - return x[0], (*readBuf)(&y) -} - -func (cn *conn) ssl(o Values) { - tlsConf := tls.Config{} - switch mode := o.Get("sslmode"); mode { - case "require", "": - tlsConf.InsecureSkipVerify = true - case "verify-full": - // fall out - case "disable": - return - default: - errorf(`unsupported sslmode %q; only "require" (default), "verify-full", and "disable" supported`, mode) - } - - w := newWriteBuf(0) - w.int32(80877103) - cn.send(w) - - b := make([]byte, 1) - _, err := io.ReadFull(cn.c, b) - if err != nil { - panic(err) - } - - if b[0] != 'S' { - panic(ErrSSLNotSupported) - } - - cn.c = tls.Client(cn.c, &tlsConf) -} - -func (cn *conn) startup(o Values) { - w := newWriteBuf(0) - w.int32(196608) - w.string("user") - w.string(o.Get("user")) - w.string("database") - w.string(o.Get("dbname")) - w.string("") - cn.send(w) - - for { - t, r := cn.recv() - switch t { - case 'K', 'S': - case 'R': - cn.auth(r, o) - case 'Z': - return - default: - errorf("unknown response for startup: %q", t) - } - } -} - -func (cn *conn) auth(r *readBuf, o Values) { - switch code := r.int32(); code { - case 0: - // OK - case 5: - s := string(r.next(4)) - w := newWriteBuf('p') - w.string("md5" + md5s(md5s(o.Get("password")+o.Get("user"))+s)) - cn.send(w) - - t, r := cn.recv() - if t != 'R' { - errorf("unexpected password response: %q", t) - } - - if r.int32() != 0 { - errorf("unexpected authentication resoonse: %q", t) - } - default: - errorf("unknown authentication response: %d", code) - } -} - -type stmt struct { - cn *conn - name string - cols []string - nparams int - ooid []int - closed bool -} - -func (st *stmt) Close() (err error) { - if st.closed { - return nil - } - - defer errRecover(&err) - - w := newWriteBuf('C') - w.byte('S') - w.string(st.name) - st.cn.send(w) - - st.cn.send(newWriteBuf('S')) - - t, _ := st.cn.recv() - if t != '3' { - errorf("unexpected close response: %q", t) - } - st.closed = true - - t, _ = st.cn.recv() - if t != 'Z' { - errorf("expected ready for query, but got: %q", t) - } - - return nil -} - -func (st *stmt) Query(v []driver.Value) (_ driver.Rows, err error) { - defer errRecover(&err) - st.exec(v) - return &rows{st: st}, nil -} - -func (st *stmt) Exec(v []driver.Value) (res driver.Result, err error) { - defer errRecover(&err) - st.exec(v) - - for { - t, r := st.cn.recv1() - switch t { - case 'E': - err = parseError(r) - case 'C': - res = parseComplete(r.string()) - case 'Z': - // done - return - case 'D': - errorf("unexpected data row returned in Exec; check your query") - case 'S', 'N': - // Ignore - default: - errorf("unknown exec response: %q", t) - } - } - - panic("not reached") -} - -func (st *stmt) exec(v []driver.Value) { - w := newWriteBuf('B') - w.string("") - w.string(st.name) - w.int16(0) - w.int16(len(v)) - for _, x := range v { - if x == nil { - w.int32(-1) - } else { - b := encode(x) - w.int32(len(b)) - w.bytes(b) - } - } - w.int16(0) - st.cn.send(w) - - w = newWriteBuf('E') - w.string("") - w.int32(0) - st.cn.send(w) - - st.cn.send(newWriteBuf('S')) - - t, _ := st.cn.recv() - if t != '2' { - errorf("unexpected bind response: %q", t) - } -} - -func (st *stmt) NumInput() int { - return st.nparams -} - -type result int64 - -func (i result) RowsAffected() (int64, error) { - return int64(i), nil -} - -func (i result) LastInsertId() (int64, error) { - return 0, ErrNotSupported -} - -func parseComplete(s string) driver.Result { - parts := strings.Split(s, " ") - n, _ := strconv.ParseInt(parts[len(parts)-1], 10, 64) - return result(n) -} - -type rows struct { - st *stmt - done bool -} - -func (rs *rows) Close() error { - for { - err := rs.Next(nil) - switch err { - case nil: - case io.EOF: - return nil - default: - return err - } - } - panic("not reached") -} - -func (rs *rows) Columns() []string { - return rs.st.cols -} - -func (rs *rows) Next(dest []driver.Value) (err error) { - if rs.done { - return io.EOF - } - - defer errRecover(&err) - - for { - t, r := rs.st.cn.recv() - switch t { - case 'C', 'S': - continue - case 'Z': - rs.done = true - return io.EOF - case 'D': - n := r.int16() - for i := 0; i < len(dest) && i < n; i++ { - l := r.int32() - if l == -1 { - continue - } - dest[i] = decode(r.next(l), rs.st.ooid[i]) - } - return - default: - errorf("unexpected message after execute: %q", t) - } - } - - panic("not reached") -} - -func md5s(s string) string { - h := md5.New() - h.Write([]byte(s)) - return fmt.Sprintf("%x", h.Sum(nil)) -} diff --git a/src/github.com/bmizerany/pq/conn_test.go b/src/github.com/bmizerany/pq/conn_test.go deleted file mode 100644 index 972cdd2..0000000 --- a/src/github.com/bmizerany/pq/conn_test.go +++ /dev/null @@ -1,299 +0,0 @@ -package pq - -import ( - "database/sql" - "database/sql/driver" - "io" - "reflect" - "testing" - "time" -) - -var cs = "user=pqgotest sslmode=disable" - -func TestExec(t *testing.T) { - db, err := sql.Open("postgres", cs) - if err != nil { - t.Fatal(err) - } - defer db.Close() - - db.Exec("DELETE FROM temp") - - r, err := db.Exec("INSERT INTO temp VALUES (1)") - if err != nil { - t.Fatal(err) - } - - if n, _ := r.RowsAffected(); n != 1 { - t.Fatalf("expected 1 row affected, not %d", n) - } -} - -func TestStatment(t *testing.T) { - db, err := sql.Open("postgres", cs) - if err != nil { - t.Fatal(err) - } - defer db.Close() - - st, err := db.Prepare("SELECT 1") - if err != nil { - t.Fatal(err) - } - - st1, err := db.Prepare("SELECT 2") - if err != nil { - t.Fatal(err) - } - - r, err := st.Query() - if err != nil { - t.Fatal(err) - } - - if !r.Next() { - t.Fatal("expected row") - } - - var i int - err = r.Scan(&i) - if err != nil { - t.Fatal(err) - } - - if i != 1 { - t.Fatalf("expected 1, got %d", i) - } - - // st1 - - r, err = st1.Query() - if err != nil { - t.Fatal(err) - } - - if !r.Next() { - if r.Err() != nil { - t.Fatal(r.Err()) - } - t.Fatal("expected row") - } - - err = r.Scan(&i) - if err != nil { - t.Fatal(err) - } - - if i != 2 { - t.Fatalf("expected 2, got %d", i) - } -} - -func TestRowsCloseBeforeDone(t *testing.T) { - db, err := sql.Open("postgres", cs) - if err != nil { - t.Fatal(err) - } - - r, err := db.Query("SELECT 1") - if err != nil { - t.Fatal(err) - } - - err = r.Close() - if err != nil { - t.Fatal(err) - } - - if r.Next() { - t.Fatal("unexpected row") - } - - if r.Err() != nil { - t.Fatal(r.Err()) - } -} - -func TestEncodeDecode(t *testing.T) { - db, err := sql.Open("postgres", cs) - if err != nil { - t.Fatal(err) - } - defer db.Close() - - q := ` - SELECT - '\x000102'::bytea, - 'foobar'::text, - NULL::integer, - '2000-1-1 01:02:03.04-7'::timestamptz - WHERE - '\x000102'::bytea = $1 - AND 'foobar'::text = $2 - AND $3::integer is NULL - ` - // AND '2000-1-1 12:00:00.000000-7'::timestamp = $3 - - exp1 := []byte{0, 1, 2} - exp2 := "foobar" - - r, err := db.Query(q, exp1, exp2, nil) - if err != nil { - t.Fatal(err) - } - defer r.Close() - - if !r.Next() { - if r.Err() != nil { - t.Fatal(r.Err()) - } - t.Fatal("expected row") - } - - var got1 []byte - var got2 string - var got3 = sql.NullInt64{Valid: true} - var got4 time.Time - - err = r.Scan(&got1, &got2, &got3, &got4) - if err != nil { - t.Fatal(err) - } - - if !reflect.DeepEqual(exp1, got1) { - t.Errorf("expected %q byte: %q", exp1, got1) - } - - if !reflect.DeepEqual(exp2, got2) { - t.Errorf("expected %q byte: %q", exp2, got2) - } - - if got3.Valid { - t.Fatal("expected invalid") - } - - if got4.Year() != 2000 { - t.Fatal("wrong year") - } -} - -func TestNoData(t *testing.T) { - db, err := sql.Open("postgres", cs) - if err != nil { - t.Fatal(err) - } - defer db.Close() - - st, err := db.Prepare("SELECT 1 WHERE true = false") - if err != nil { - t.Fatal(err) - } - defer st.Close() - - r, err := st.Query() - if err != nil { - t.Fatal(err) - } - defer r.Close() - - if r.Next() { - if r.Err() != nil { - t.Fatal(r.Err()) - } - t.Fatal("unexpected row") - } -} - -func TestPGError(t *testing.T) { - db, err := sql.Open("postgres", "user=asdf") - if err != nil { - t.Fatal(err) - } - defer db.Close() - - _, err = db.Begin() - if err == nil { - t.Fatal("expected error") - } - - if err != driver.ErrBadConn { - t.Fatalf("expected a PGError, got: %v", err) - } -} - -func TestBadConn(t *testing.T) { - var err error - - func() { - defer errRecover(&err) - panic(io.EOF) - }() - - if err != driver.ErrBadConn { - t.Fatalf("expected driver.ErrBadConn, got: %#v", err) - } - - func() { - defer errRecover(&err) - e := &PGError{c: make(map[byte]string)} - e.c['S'] = Efatal - panic(e) - }() - - if err != driver.ErrBadConn { - t.Fatalf("expected driver.ErrBadConn, got: %#v", err) - } -} - -func TestErrorOnExec(t *testing.T) { - db, err := sql.Open("postgres", cs) - if err != nil { - t.Fatal(err) - } - defer db.Close() - - sql := "DO $$BEGIN RAISE unique_violation USING MESSAGE='foo'; END; $$;" - _, err = db.Exec(sql) - _, ok := err.(*PGError) - if !ok { - t.Fatalf("expected PGError, was: %#v", err) - } - - _, err = db.Exec("SELECT 1 WHERE true = false") // returns no rows - if err != nil { - t.Fatal(err) - } -} - -func TestErrorOnQuery(t *testing.T) { - db, err := sql.Open("postgres", cs) - if err != nil { - t.Fatal(err) - } - defer db.Close() - - sql := "DO $$BEGIN RAISE unique_violation USING MESSAGE='foo'; END; $$;" - r, err := db.Query(sql) - if err != nil { - t.Fatal(err) - } - - if r.Next() { - t.Fatal("unexpected row, want error") - } - - _, ok := r.Err().(*PGError) - if !ok { - t.Fatalf("expected PGError, was: %#v", r.Err()) - } - - r, err = db.Query("SELECT 1 WHERE true = false") // returns no rows - if err != nil { - t.Fatal(err) - } - - if r.Next() { - t.Fatal("unexpected row") - } -} diff --git a/src/github.com/bmizerany/pq/encode.go b/src/github.com/bmizerany/pq/encode.go deleted file mode 100644 index da4d1d4..0000000 --- a/src/github.com/bmizerany/pq/encode.go +++ /dev/null @@ -1,83 +0,0 @@ -package pq - -import ( - "database/sql/driver" - "encoding/hex" - "fmt" - "time" -) - -func encode(x interface{}) []byte { - const timeFormat = "2006-01-02 15:04:05.0000-07" - - switch v := x.(type) { - case int64: - return []byte(fmt.Sprintf("%d", v)) - case float32, float64: - return []byte(fmt.Sprintf("%f", v)) - case []byte: - return []byte(fmt.Sprintf("\\x%x", v)) - case string: - return []byte(v) - case bool: - return []byte(fmt.Sprintf("%t", v)) - case time.Time: - return []byte(v.Format(timeFormat)) - default: - errorf("encode: unknown type for %T", v) - } - - panic("not reached") -} - -func decode(s []byte, typ int) interface{} { - switch typ { - case t_bytea: - s = s[2:] // trim off "\\x" - d := make([]byte, hex.DecodedLen(len(s))) - _, err := hex.Decode(d, s) - if err != nil { - errorf("%s", err) - } - return d - case t_timestamptz: - return mustParse("2006-01-02 15:04:05-07", s) - case t_timestamp: - return mustParse("2006-01-02 15:04:05", s) - case t_time: - return mustParse("15:04:05", s) - case t_timetz: - return mustParse("15:04:05-07", s) - case t_date: - return mustParse("2006-01-02", s) - } - - return s -} - -func mustParse(f string, s []byte) time.Time { - t, err := time.Parse(f, string(s)) - if err != nil { - errorf("decode: %s", err) - } - return t -} - -type NullTime struct { - Time time.Time - Valid bool // Valid is true if Time is not NULL -} - -// Scan implements the Scanner interface. -func (nt *NullTime) Scan(value interface{}) error { - nt.Time, nt.Valid = value.(time.Time) - return nil -} - -// Value implements the driver Valuer interface. -func (nt NullTime) Value() (driver.Value, error) { - if !nt.Valid { - return nil, nil - } - return nt.Time, nil -} diff --git a/src/github.com/bmizerany/pq/encode_test.go b/src/github.com/bmizerany/pq/encode_test.go deleted file mode 100644 index fb1723c..0000000 --- a/src/github.com/bmizerany/pq/encode_test.go +++ /dev/null @@ -1,26 +0,0 @@ -package pq - -import ( - "testing" - "time" -) - -func TestScanTimestamp(t *testing.T) { - var nt NullTime - tn := time.Now() - (&nt).Scan(tn) - if !nt.Valid { - t.Errorf("Expected Valid=false") - } - if nt.Time != tn { - t.Errorf("Time value mismatch") - } -} - -func TestScanNilTimestamp(t *testing.T) { - var nt NullTime - (&nt).Scan(nil) - if nt.Valid { - t.Errorf("Expected Valid=false") - } -} diff --git a/src/github.com/bmizerany/pq/error.go b/src/github.com/bmizerany/pq/error.go deleted file mode 100644 index 2a68a0e..0000000 --- a/src/github.com/bmizerany/pq/error.go +++ /dev/null @@ -1,78 +0,0 @@ -package pq - -import ( - "database/sql/driver" - "fmt" - "io" - "runtime" -) - -const ( - Efatal = "FATAL" - Epanic = "PANIC" - Ewarning = "WARNING" - Enotice = "NOTICE" - Edebug = "DEBUG" - Einfo = "INFO" - Elog = "LOG" -) - -type Error error - -type PGError struct { - c map[byte]string -} - -func parseError(r *readBuf) *PGError { - err := &PGError{make(map[byte]string)} - for t := r.byte(); t != 0; t = r.byte() { - err.c[t] = r.string() - } - return err -} - -func (err *PGError) Get(k byte) (v string) { - v, _ = err.c[k] - return -} - -func (err *PGError) Fatal() bool { - return err.Get('S') == Efatal -} - -func (err *PGError) Error() string { - var s string - for k, v := range err.c { - s += fmt.Sprintf(" %c:%q", k, v) - } - return "pq: " + s[1:] -} - -func errorf(s string, args ...interface{}) { - panic(Error(fmt.Errorf("pq: %s", fmt.Sprintf(s, args...)))) -} - -func errRecover(err *error) { - e := recover() - switch v := e.(type) { - case nil: - // Do nothing - case runtime.Error: - panic(v) - case *PGError: - if v.Fatal() { - *err = driver.ErrBadConn - } else { - *err = v - } - case error: - if v == io.EOF { - *err = driver.ErrBadConn - } else { - *err = v - } - - default: - panic(fmt.Sprintf("unknown error: %#v", e)) - } -} diff --git a/src/github.com/bmizerany/pq/types.go b/src/github.com/bmizerany/pq/types.go deleted file mode 100644 index d37aaca..0000000 --- a/src/github.com/bmizerany/pq/types.go +++ /dev/null @@ -1,317 +0,0 @@ -package pq - -const ( - t_bool = 16 - t_bytea = 17 - t_char = 18 - t_name = 19 - t_int8 = 20 - t_int2 = 21 - t_int2vector = 22 - t_int4 = 23 - t_regproc = 24 - t_text = 25 - t_oid = 26 - t_tid = 27 - t_xid = 28 - t_cid = 29 - t_oidvector = 30 - t_pg_type = 71 - t_pg_attribute = 75 - t_pg_proc = 81 - t_pg_class = 83 - t_xml = 142 - t__xml = 143 - t_pg_node_tree = 194 - t_smgr = 210 - t_point = 600 - t_lseg = 601 - t_path = 602 - t_box = 603 - t_polygon = 604 - t_line = 628 - t__line = 629 - t_float4 = 700 - t_float8 = 701 - t_abstime = 702 - t_reltime = 703 - t_tinterval = 704 - t_unknown = 705 - t_circle = 718 - t__circle = 719 - t_money = 790 - t__money = 791 - t_macaddr = 829 - t_inet = 869 - t_cidr = 650 - t__bool = 1000 - t__bytea = 1001 - t__char = 1002 - t__name = 1003 - t__int2 = 1005 - t__int2vector = 1006 - t__int4 = 1007 - t__regproc = 1008 - t__text = 1009 - t__oid = 1028 - t__tid = 1010 - t__xid = 1011 - t__cid = 1012 - t__oidvector = 1013 - t__bpchar = 1014 - t__varchar = 1015 - t__int8 = 1016 - t__point = 1017 - t__lseg = 1018 - t__path = 1019 - t__box = 1020 - t__float4 = 1021 - t__float8 = 1022 - t__abstime = 1023 - t__reltime = 1024 - t__tinterval = 1025 - t__polygon = 1027 - t_aclitem = 1033 - t__aclitem = 1034 - t__macaddr = 1040 - t__inet = 1041 - t__cidr = 651 - t__cstring = 1263 - t_bpchar = 1042 - t_varchar = 1043 - t_date = 1082 - t_time = 1083 - t_timestamp = 1114 - t__timestamp = 1115 - t__date = 1182 - t__time = 1183 - t_timestamptz = 1184 - t__timestamptz = 1185 - t_interval = 1186 - t__interval = 1187 - t__numeric = 1231 - t_timetz = 1266 - t__timetz = 1270 - t_bit = 1560 - t__bit = 1561 - t_varbit = 1562 - t__varbit = 1563 - t_numeric = 1700 - t_refcursor = 1790 - t__refcursor = 2201 - t_regprocedure = 2202 - t_regoper = 2203 - t_regoperator = 2204 - t_regclass = 2205 - t_regtype = 2206 - t__regprocedure = 2207 - t__regoper = 2208 - t__regoperator = 2209 - t__regclass = 2210 - t__regtype = 2211 - t_uuid = 2950 - t__uuid = 2951 - t_tsvector = 3614 - t_gtsvector = 3642 - t_tsquery = 3615 - t_regconfig = 3734 - t_regdictionary = 3769 - t__tsvector = 3643 - t__gtsvector = 3644 - t__tsquery = 3645 - t__regconfig = 3735 - t__regdictionary = 3770 - t_txid_snapshot = 2970 - t__txid_snapshot = 2949 - t_record = 2249 - t__record = 2287 - t_cstring = 2275 - t_any = 2276 - t_anyarray = 2277 - t_void = 2278 - t_trigger = 2279 - t_language_handler = 2280 - t_internal = 2281 - t_opaque = 2282 - t_anyelement = 2283 - t_anynonarray = 2776 - t_anyenum = 3500 - t_fdw_handler = 3115 - t_pg_attrdef = 10000 - t_pg_constraint = 10001 - t_pg_inherits = 10002 - t_pg_index = 10003 - t_pg_operator = 10004 - t_pg_opfamily = 10005 - t_pg_opclass = 10006 - t_pg_am = 10117 - t_pg_amop = 10118 - t_pg_amproc = 10478 - t_pg_language = 10731 - t_pg_largeobject_metadata = 10732 - t_pg_largeobject = 10733 - t_pg_aggregate = 10734 - t_pg_statistic = 10735 - t_pg_rewrite = 10736 - t_pg_trigger = 10737 - t_pg_description = 10738 - t_pg_cast = 10739 - t_pg_enum = 10936 - t_pg_namespace = 10937 - t_pg_conversion = 10938 - t_pg_depend = 10939 - t_pg_database = 1248 - t_pg_db_role_setting = 10940 - t_pg_tablespace = 10941 - t_pg_pltemplate = 10942 - t_pg_authid = 2842 - t_pg_auth_members = 2843 - t_pg_shdepend = 10943 - t_pg_shdescription = 10944 - t_pg_ts_config = 10945 - t_pg_ts_config_map = 10946 - t_pg_ts_dict = 10947 - t_pg_ts_parser = 10948 - t_pg_ts_template = 10949 - t_pg_extension = 10950 - t_pg_foreign_data_wrapper = 10951 - t_pg_foreign_server = 10952 - t_pg_user_mapping = 10953 - t_pg_foreign_table = 10954 - t_pg_default_acl = 10955 - t_pg_seclabel = 10956 - t_pg_collation = 10957 - t_pg_toast_2604 = 10958 - t_pg_toast_2606 = 10959 - t_pg_toast_2609 = 10960 - t_pg_toast_1255 = 10961 - t_pg_toast_2618 = 10962 - t_pg_toast_3596 = 10963 - t_pg_toast_2619 = 10964 - t_pg_toast_2620 = 10965 - t_pg_toast_1262 = 10966 - t_pg_toast_2396 = 10967 - t_pg_toast_2964 = 10968 - t_pg_roles = 10970 - t_pg_shadow = 10973 - t_pg_group = 10976 - t_pg_user = 10979 - t_pg_rules = 10982 - t_pg_views = 10986 - t_pg_tables = 10989 - t_pg_indexes = 10993 - t_pg_stats = 10997 - t_pg_locks = 11001 - t_pg_cursors = 11004 - t_pg_available_extensions = 11007 - t_pg_available_extension_versions = 11010 - t_pg_prepared_xacts = 11013 - t_pg_prepared_statements = 11017 - t_pg_seclabels = 11020 - t_pg_settings = 11024 - t_pg_timezone_abbrevs = 11029 - t_pg_timezone_names = 11032 - t_pg_stat_all_tables = 11035 - t_pg_stat_xact_all_tables = 11039 - t_pg_stat_sys_tables = 11043 - t_pg_stat_xact_sys_tables = 11047 - t_pg_stat_user_tables = 11050 - t_pg_stat_xact_user_tables = 11054 - t_pg_statio_all_tables = 11057 - t_pg_statio_sys_tables = 11061 - t_pg_statio_user_tables = 11064 - t_pg_stat_all_indexes = 11067 - t_pg_stat_sys_indexes = 11071 - t_pg_stat_user_indexes = 11074 - t_pg_statio_all_indexes = 11077 - t_pg_statio_sys_indexes = 11081 - t_pg_statio_user_indexes = 11084 - t_pg_statio_all_sequences = 11087 - t_pg_statio_sys_sequences = 11090 - t_pg_statio_user_sequences = 11093 - t_pg_stat_activity = 11096 - t_pg_stat_replication = 11099 - t_pg_stat_database = 11102 - t_pg_stat_database_conflicts = 11105 - t_pg_stat_user_functions = 11108 - t_pg_stat_xact_user_functions = 11112 - t_pg_stat_bgwriter = 11116 - t_pg_user_mappings = 11119 - t_cardinal_number = 11669 - t_character_data = 11671 - t_sql_identifier = 11672 - t_information_schema_catalog_name = 11674 - t_time_stamp = 11676 - t_yes_or_no = 11677 - t_applicable_roles = 11680 - t_administrable_role_authorizations = 11684 - t_attributes = 11687 - t_character_sets = 11691 - t_check_constraint_routine_usage = 11695 - t_check_constraints = 11699 - t_collations = 11703 - t_collation_character_set_applicability = 11706 - t_column_domain_usage = 11709 - t_column_privileges = 11713 - t_column_udt_usage = 11717 - t_columns = 11721 - t_constraint_column_usage = 11725 - t_constraint_table_usage = 11729 - t_domain_constraints = 11733 - t_domain_udt_usage = 11737 - t_domains = 11740 - t_enabled_roles = 11744 - t_key_column_usage = 11747 - t_parameters = 11751 - t_referential_constraints = 11755 - t_role_column_grants = 11759 - t_routine_privileges = 11762 - t_role_routine_grants = 11766 - t_routines = 11769 - t_schemata = 11773 - t_sequences = 11776 - t_sql_features = 11780 - t_pg_toast_11779 = 11782 - t_sql_implementation_info = 11785 - t_pg_toast_11784 = 11787 - t_sql_languages = 11790 - t_pg_toast_11789 = 11792 - t_sql_packages = 11795 - t_pg_toast_11794 = 11797 - t_sql_parts = 11800 - t_pg_toast_11799 = 11802 - t_sql_sizing = 11805 - t_pg_toast_11804 = 11807 - t_sql_sizing_profiles = 11810 - t_pg_toast_11809 = 11812 - t_table_constraints = 11815 - t_table_privileges = 11819 - t_role_table_grants = 11823 - t_tables = 11826 - t_triggered_update_columns = 11830 - t_triggers = 11834 - t_usage_privileges = 11838 - t_role_usage_grants = 11842 - t_view_column_usage = 11845 - t_view_routine_usage = 11849 - t_view_table_usage = 11853 - t_views = 11857 - t_data_type_privileges = 11861 - t_element_types = 11865 - t__pg_foreign_data_wrappers = 11869 - t_foreign_data_wrapper_options = 11872 - t_foreign_data_wrappers = 11875 - t__pg_foreign_servers = 11878 - t_foreign_server_options = 11882 - t_foreign_servers = 11885 - t__pg_foreign_tables = 11888 - t_foreign_table_options = 11892 - t_foreign_tables = 11895 - t__pg_user_mappings = 11898 - t_user_mapping_options = 11901 - t_user_mappings = 11905 - t_t = 16806 - t__t = 16805 - t_temp = 16810 - t__temp = 16809 -) diff --git a/src/github.com/bmizerany/pq/url.go b/src/github.com/bmizerany/pq/url.go deleted file mode 100644 index 1e835d0..0000000 --- a/src/github.com/bmizerany/pq/url.go +++ /dev/null @@ -1,69 +0,0 @@ -package pq - -import ( - "fmt" - nurl "net/url" - "strings" - "sort" -) - -// ParseURL converts url to a connection string for driver.Open. -// Example: -// -// "postgres://bob:secret@1.2.3.4:5432/mydb?sslmode=verify-full" -// -// converts to: -// -// "user=bob password=secret host=1.2.3.4 port=5432 dbname=mydb sslmode=verify-full" -// -// A minimal example: -// -// "postgres://" -// -// This will be blank, causing driver.Open to use all of the defaults -func ParseURL(url string) (string, error) { - u, err := nurl.Parse(url) - if err != nil { - return "", err - } - - if u.Scheme != "postgres" { - return "", fmt.Errorf("invalid connection protocol: %s", u.Scheme) - } - - var kvs []string - if u.User != nil { - v := u.User.Username() - kvs = appendkv(kvs, "user", v) - - v, _ = u.User.Password() - kvs = appendkv(kvs, "password", v) - } - - i := strings.Index(u.Host, ":") - if i < 0 { - kvs = appendkv(kvs, "host", u.Host) - } else { - kvs = appendkv(kvs, "host", u.Host[:i]) - kvs = appendkv(kvs, "port", u.Host[i+1:]) - } - - if u.Path != "" { - kvs = appendkv(kvs, "dbname", u.Path[1:]) - } - - q := u.Query() - for k, _ := range q { - kvs = appendkv(kvs, k, q.Get(k)) - } - - sort.Strings(kvs) // Makes testing easier (not a performance concern) - return strings.Join(kvs, " "), nil -} - -func appendkv(kvs []string, k, v string) []string { - if v != "" { - return append(kvs, k+"="+v) - } - return kvs -} diff --git a/src/github.com/bmizerany/pq/url_test.go b/src/github.com/bmizerany/pq/url_test.go deleted file mode 100644 index ef77ef0..0000000 --- a/src/github.com/bmizerany/pq/url_test.go +++ /dev/null @@ -1,53 +0,0 @@ -package pq - -import ( - "testing" -) - -func TestSimpleParseURL(t *testing.T) { - expected := "host=hostname.remote" - str, err := ParseURL("postgres://hostname.remote") - if err != nil { - t.Fatal(err) - } - - if str != expected { - t.Fatalf("unexpected result from ParseURL:\n+ %v\n- %v", str, expected) - } -} - -func TestFullParseURL(t *testing.T) { - expected := "dbname=database host=hostname.remote password=secret port=1234 user=username" - str, err := ParseURL("postgres://username:secret@hostname.remote:1234/database") - if err != nil { - t.Fatal(err) - } - - if str != expected { - t.Fatalf("unexpected result from ParseURL:\n+ %s\n- %s", str, expected) - } -} - -func TestInvalidProtocolParseURL(t *testing.T) { - _, err := ParseURL("http://hostname.remote/") - switch err { - case nil: - t.Fatal("Expected an error from parsing invalid protocol") - default: - msg := "invalid connection protocol: http" - if err.Error() != msg { - t.Fatal("Unexpected error message:\n+ %s\n- %s", err.Error(), msg) - } - } -} - -func TestMinimalURL(t *testing.T) { - cs, err := ParseURL("postgres://") - if err != nil { - t.Fatal(err) - } - - if cs != "" { - t.Fatalf("expected blank connection string, got: %q", cs) - } -} diff --git a/src/github.com/mattn/go-sqlite3/Makefile b/src/github.com/mattn/go-sqlite3/Makefile deleted file mode 100644 index 4f78cd9..0000000 --- a/src/github.com/mattn/go-sqlite3/Makefile +++ /dev/null @@ -1,6 +0,0 @@ -include $(GOROOT)/src/Make.inc - -TARG = github.com/mattn/go-sqlite3 -CGOFILES = sqlite3.go - -include $(GOROOT)/src/Make.pkg diff --git a/src/github.com/mattn/go-sqlite3/README.mkd b/src/github.com/mattn/go-sqlite3/README.mkd deleted file mode 100644 index baa786a..0000000 --- a/src/github.com/mattn/go-sqlite3/README.mkd +++ /dev/null @@ -1,12 +0,0 @@ -go-sqlite3 -========== - -DESCRIPTION ------------ - -sqlite3 driver for go that using exp/sql - -LICENSE -------- - -MIT: http://mattn.mit-license.org/2011 diff --git a/src/github.com/mattn/go-sqlite3/example/Makefile b/src/github.com/mattn/go-sqlite3/example/Makefile deleted file mode 100644 index 0d12cfc..0000000 --- a/src/github.com/mattn/go-sqlite3/example/Makefile +++ /dev/null @@ -1,6 +0,0 @@ -include $(GOROOT)/src/Make.inc - -TARG = main -GOFILES = main.go - -include $(GOROOT)/src/Make.cmd diff --git a/src/github.com/mattn/go-sqlite3/example/main.go b/src/github.com/mattn/go-sqlite3/example/main.go deleted file mode 100644 index 6d6aa0e..0000000 --- a/src/github.com/mattn/go-sqlite3/example/main.go +++ /dev/null @@ -1,65 +0,0 @@ -package main - -import ( - "database/sql" - "fmt" - _ "github.com/mattn/go-sqlite3" - "os" -) - -func main() { - os.Remove("./foo.db") - - db, err := sql.Open("sqlite3", "./foo.db") - if err != nil { - fmt.Println(err) - return - } - - sqls := []string{ - "create table foo (id integer not null primary key, name text)", - "delete from foo", - } - for _, sql := range sqls { - _, err = db.Exec(sql) - if err != nil { - fmt.Printf("%q: %s\n", err, sql) - return - } - } - - tx, err := db.Begin() - if err != nil { - fmt.Println(err) - return - } - stmt, err := tx.Prepare("insert into foo(id, name) values(?, ?)") - if err != nil { - fmt.Println(err) - return - } - defer stmt.Close() - - for i := 0; i < 100; i++ { - _, err = stmt.Exec(i, fmt.Sprintf("こんにちわ世界%03d", i)) - if err != nil { - fmt.Println(err) - return - } - } - tx.Commit() - - rows, err := db.Query("select id, name from foo") - if err != nil { - fmt.Println(err) - return - } - defer rows.Close() - - for rows.Next() { - var id int - var name string - rows.Scan(&id, &name) - println(id, name) - } -} diff --git a/src/github.com/mattn/go-sqlite3/sqlite3.go b/src/github.com/mattn/go-sqlite3/sqlite3.go deleted file mode 100644 index 12a0bf6..0000000 --- a/src/github.com/mattn/go-sqlite3/sqlite3.go +++ /dev/null @@ -1,282 +0,0 @@ -package sqlite - -/* -#include -#include -#include - -static int -_sqlite3_bind_text(sqlite3_stmt *stmt, int n, char *p, int np) { - return sqlite3_bind_text(stmt, n, p, np, SQLITE_TRANSIENT); -} - -static int -_sqlite3_bind_blob(sqlite3_stmt *stmt, int n, void *p, int np) { - return sqlite3_bind_blob(stmt, n, p, np, SQLITE_TRANSIENT); -} - -#cgo pkg-config: sqlite3 -*/ -import "C" -import ( - "database/sql" - "database/sql/driver" - "errors" - "io" - "unsafe" -) - -func init() { - sql.Register("sqlite3", &SQLiteDriver{}) -} - -type SQLiteDriver struct { -} - -type SQLiteConn struct { - db *C.sqlite3 -} - -type SQLiteTx struct { - c *SQLiteConn -} - -func (tx *SQLiteTx) Commit() error { - if err := tx.c.exec("COMMIT"); err != nil { - return err - } - return nil -} - -func (tx *SQLiteTx) Rollback() error { - if err := tx.c.exec("ROLLBACK"); err != nil { - return err - } - return nil -} - -func (c *SQLiteConn) exec(cmd string) error { - pcmd := C.CString(cmd) - defer C.free(unsafe.Pointer(pcmd)) - rv := C.sqlite3_exec(c.db, pcmd, nil, nil, nil) - if rv != C.SQLITE_OK { - return errors.New(C.GoString(C.sqlite3_errmsg(c.db))) - } - return nil -} - -func (c *SQLiteConn) Begin() (driver.Tx, error) { - if err := c.exec("BEGIN"); err != nil { - return nil, err - } - return &SQLiteTx{c}, nil -} - -func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) { - if C.sqlite3_threadsafe() == 0 { - return nil, errors.New("sqlite library was not compiled for thread-safe operation") - } - - var db *C.sqlite3 - name := C.CString(dsn) - defer C.free(unsafe.Pointer(name)) - rv := C.sqlite3_open_v2(name, &db, - C.SQLITE_OPEN_FULLMUTEX| - C.SQLITE_OPEN_READWRITE| - C.SQLITE_OPEN_CREATE, - nil) - if rv != 0 { - return nil, errors.New(C.GoString(C.sqlite3_errmsg(db))) - } - if db == nil { - return nil, errors.New("sqlite succeeded without returning a database") - } - rv = C.sqlite3_busy_timeout(db, 5000) - if rv != C.SQLITE_OK { - return nil, errors.New(C.GoString(C.sqlite3_errmsg(db))) - } - return &SQLiteConn{db}, nil -} - -func (c *SQLiteConn) Close() error { - s := C.sqlite3_next_stmt(c.db, nil) - for s != nil { - C.sqlite3_finalize(s) - s = C.sqlite3_next_stmt(c.db, s) - } - rv := C.sqlite3_close(c.db) - if rv != C.SQLITE_OK { - return errors.New("sqlite succeeded without returning a database") - } - c.db = nil - return nil -} - -type SQLiteStmt struct { - c *SQLiteConn - s *C.sqlite3_stmt - t string - closed bool -} - -func (c *SQLiteConn) Prepare(query string) (driver.Stmt, error) { - pquery := C.CString(query) - defer C.free(unsafe.Pointer(pquery)) - var s *C.sqlite3_stmt - var perror *C.char - rv := C.sqlite3_prepare_v2(c.db, pquery, -1, &s, &perror) - if rv != C.SQLITE_OK { - return nil, errors.New(C.GoString(C.sqlite3_errmsg(c.db))) - } - var t string - if perror != nil && C.strlen(perror) > 0 { - t = C.GoString(perror) - } - return &SQLiteStmt{c: c, s: s, t: t}, nil -} - -func (s *SQLiteStmt) Close() error { - if s.closed { - return nil - } - s.closed = true - rv := C.sqlite3_finalize(s.s) - if rv != C.SQLITE_OK { - return errors.New(C.GoString(C.sqlite3_errmsg(s.c.db))) - } - return nil -} - -func (s *SQLiteStmt) NumInput() int { - return int(C.sqlite3_bind_parameter_count(s.s)) -} - -func (s *SQLiteStmt) bind(args []driver.Value) error { - rv := C.sqlite3_reset(s.s) - if rv != C.SQLITE_ROW && rv != C.SQLITE_OK && rv != C.SQLITE_DONE { - return errors.New(C.GoString(C.sqlite3_errmsg(s.c.db))) - } - - for i, v := range args { - n := C.int(i + 1) - switch v := v.(type) { - case nil: - rv = C.sqlite3_bind_null(s.s, n) - case string: - if len(v) == 0 { - b := []byte{0} - rv = C._sqlite3_bind_text(s.s, n, (*C.char)(unsafe.Pointer(&b[0])), C.int(0)) - } else { - b := []byte(v) - rv = C._sqlite3_bind_text(s.s, n, (*C.char)(unsafe.Pointer(&b[0])), C.int(len(b))) - } - case int: - rv = C.sqlite3_bind_int(s.s, n, C.int(v)) - case int64: - rv = C.sqlite3_bind_int64(s.s, n, C.sqlite3_int64(v)) - case byte: - rv = C.sqlite3_bind_int(s.s, n, C.int(v)) - case bool: - if bool(v) { - rv = C.sqlite3_bind_int(s.s, n, 1) - } else { - rv = C.sqlite3_bind_int(s.s, n, 0) - } - case float32: - rv = C.sqlite3_bind_double(s.s, n, C.double(v)) - case float64: - rv = C.sqlite3_bind_double(s.s, n, C.double(v)) - case []byte: - var p *byte - if len(v) > 0 { - p = &v[0] - } - rv = C._sqlite3_bind_blob(s.s, n, unsafe.Pointer(p), C.int(len(v))) - } - if rv != C.SQLITE_OK { - return errors.New(C.GoString(C.sqlite3_errmsg(s.c.db))) - } - } - return nil -} - -func (s *SQLiteStmt) Query(args []driver.Value) (driver.Rows, error) { - if err := s.bind(args); err != nil { - return nil, err - } - return &SQLiteRows{s, int(C.sqlite3_column_count(s.s)), nil}, nil -} - -type SQLiteResult struct { - s *SQLiteStmt -} - -func (r *SQLiteResult) LastInsertId() (int64, error) { - return int64(C.sqlite3_last_insert_rowid(r.s.c.db)), nil -} - -func (r *SQLiteResult) RowsAffected() (int64, error) { - return int64(C.sqlite3_changes(r.s.c.db)), nil -} - -func (s *SQLiteStmt) Exec(args []driver.Value) (driver.Result, error) { - if err := s.bind(args); err != nil { - return nil, err - } - rv := C.sqlite3_step(s.s) - if rv != C.SQLITE_ROW && rv != C.SQLITE_OK && rv != C.SQLITE_DONE { - return nil, errors.New(C.GoString(C.sqlite3_errmsg(s.c.db))) - } - return &SQLiteResult{s}, nil -} - -type SQLiteRows struct { - s *SQLiteStmt - nc int - cols []string -} - -func (rc *SQLiteRows) Close() error { - rv := C.sqlite3_reset(rc.s.s) - if rv != C.SQLITE_OK { - return errors.New(C.GoString(C.sqlite3_errmsg(rc.s.c.db))) - } - return nil -} - -func (rc *SQLiteRows) Columns() []string { - if rc.nc != len(rc.cols) { - rc.cols = make([]string, rc.nc) - for i := 0; i < rc.nc; i++ { - rc.cols[i] = C.GoString(C.sqlite3_column_name(rc.s.s, C.int(i))) - } - } - return rc.cols -} - -func (rc *SQLiteRows) Next(dest []driver.Value) error { - rv := C.sqlite3_step(rc.s.s) - if rv == C.SQLITE_DONE { - return io.EOF - } - if rv != C.SQLITE_ROW { - return errors.New(C.GoString(C.sqlite3_errmsg(rc.s.c.db))) - } - for i := range dest { - switch C.sqlite3_column_type(rc.s.s, C.int(i)) { - case C.SQLITE_INTEGER: - dest[i] = int64(C.sqlite3_column_int64(rc.s.s, C.int(i))) - case C.SQLITE_FLOAT: - dest[i] = float64(C.sqlite3_column_double(rc.s.s, C.int(i))) - case C.SQLITE_BLOB: - n := int(C.sqlite3_column_bytes(rc.s.s, C.int(i))) - p := C.sqlite3_column_blob(rc.s.s, C.int(i)) - dest[i] = (*[1 << 30]byte)(unsafe.Pointer(p))[0:n] - case C.SQLITE_NULL: - dest[i] = nil - case C.SQLITE_TEXT: - dest[i] = C.GoString((*C.char)(unsafe.Pointer(C.sqlite3_column_text(rc.s.s, C.int(i))))) - } - } - return nil -} diff --git a/src/github.com/mattn/go-sqlite3/sqlite3_test.go b/src/github.com/mattn/go-sqlite3/sqlite3_test.go deleted file mode 100644 index 7cbe8c3..0000000 --- a/src/github.com/mattn/go-sqlite3/sqlite3_test.go +++ /dev/null @@ -1,262 +0,0 @@ -package sqlite - -import ( - "database/sql" - "os" - "testing" -) - -func TestOpen(t *testing.T) { - db, err := sql.Open("sqlite3", "./foo.db") - if err != nil { - t.Errorf("Failed to open database:", err) - return - } - defer os.Remove("./foo.db") - - _, err = db.Exec("drop table foo") - _, err = db.Exec("create table foo (id integer)") - if err != nil { - t.Errorf("Failed to create table:", err) - return - } - - if stat, err := os.Stat("./foo.db"); err != nil || stat.IsDir() { - t.Errorf("Failed to create ./foo.db") - } -} - -func TestInsert(t *testing.T) { - db, err := sql.Open("sqlite3", "./foo.db") - if err != nil { - t.Errorf("Failed to open database:", err) - return - } - defer os.Remove("./foo.db") - - _, err = db.Exec("drop table foo") - _, err = db.Exec("create table foo (id integer)") - if err != nil { - t.Errorf("Failed to create table:", err) - return - } - - res, err := db.Exec("insert into foo(id) values(123)") - if err != nil { - t.Errorf("Failed to insert record:", err) - return - } - affected, _ := res.RowsAffected() - if affected != 1 { - t.Errorf("Expected %d for affected rows, but %d:", 1, affected) - return - } - - rows, err := db.Query("select id from foo") - if err != nil { - t.Errorf("Failed to select records:", err) - return - } - defer rows.Close() - - rows.Next() - - var result int - rows.Scan(&result) - if result != 123 { - t.Errorf("Fetched %q; expected %q", 123, result) - } -} - -func TestUpdate(t *testing.T) { - db, err := sql.Open("sqlite3", "./foo.db") - if err != nil { - t.Errorf("Failed to open database:", err) - return - } - defer os.Remove("./foo.db") - - _, err = db.Exec("drop table foo") - _, err = db.Exec("create table foo (id integer)") - if err != nil { - t.Errorf("Failed to create table:", err) - return - } - - res, err := db.Exec("insert into foo(id) values(123)") - if err != nil { - t.Errorf("Failed to insert record:", err) - return - } - expected, err := res.LastInsertId() - if err != nil { - t.Errorf("Failed to get LastInsertId:", err) - return - } - affected, _ := res.RowsAffected() - if err != nil { - t.Errorf("Failed to get RowsAffected:", err) - return - } - if affected != 1 { - t.Errorf("Expected %d for affected rows, but %d:", 1, affected) - return - } - - res, err = db.Exec("update foo set id = 234") - if err != nil { - t.Errorf("Failed to update record:", err) - return - } - lastId, err := res.LastInsertId() - if err != nil { - t.Errorf("Failed to get LastInsertId:", err) - return - } - if expected != lastId { - t.Errorf("Expected %q for last Id, but %q:", expected, lastId) - } - affected, _ = res.RowsAffected() - if err != nil { - t.Errorf("Failed to get RowsAffected:", err) - return - } - if affected != 1 { - t.Errorf("Expected %d for affected rows, but %d:", 1, affected) - return - } - - rows, err := db.Query("select id from foo") - if err != nil { - t.Errorf("Failed to select records:", err) - return - } - defer rows.Close() - - rows.Next() - - var result int - rows.Scan(&result) - if result != 234 { - t.Errorf("Fetched %q; expected %q", 234, result) - } -} - -func TestDelete(t *testing.T) { - db, err := sql.Open("sqlite3", "./foo.db") - if err != nil { - t.Errorf("Failed to select records:", err) - return - } - defer os.Remove("./foo.db") - - _, err = db.Exec("drop table foo") - _, err = db.Exec("create table foo (id integer)") - if err != nil { - t.Errorf("Failed to create table:", err) - return - } - - res, err := db.Exec("insert into foo(id) values(123)") - if err != nil { - t.Errorf("Failed to insert record:", err) - return - } - expected, err := res.LastInsertId() - if err != nil { - t.Errorf("Failed to get LastInsertId:", err) - return - } - affected, err := res.RowsAffected() - if err != nil { - t.Errorf("Failed to get RowsAffected:", err) - return - } - if affected != 1 { - t.Errorf("Expected %d for cout of affected rows, but %q:", 1, affected) - } - - res, err = db.Exec("delete from foo where id = 123") - if err != nil { - t.Errorf("Failed to delete record:", err) - return - } - lastId, err := res.LastInsertId() - if err != nil { - t.Errorf("Failed to get LastInsertId:", err) - return - } - if expected != lastId { - t.Errorf("Expected %q for last Id, but %q:", expected, lastId) - } - affected, err = res.RowsAffected() - if err != nil { - t.Errorf("Failed to get RowsAffected:", err) - return - } - if affected != 1 { - t.Errorf("Expected %d for cout of affected rows, but %q:", 1, affected) - } - - rows, err := db.Query("select id from foo") - if err != nil { - t.Errorf("Failed to select records:", err) - return - } - defer rows.Close() - - if rows.Next() { - t.Errorf("Fetched row but expected not rows") - } -} - -func TestBooleanRoundtrip(t *testing.T) { - db, err := sql.Open("sqlite3", "./foo.db") - if err != nil { - t.Errorf("Tailed to open database:", err) - return - } - defer os.Remove("./foo.db") - - _, err = db.Exec("DROP TABLE foo") - _, err = db.Exec("CREATE TABLE foo(id INTEGER, value BOOL)") - if err != nil { - t.Errorf("Failed to create table:", err) - return - } - - _, err = db.Exec("INSERT INTO foo(id, value) VALUES(1, ?)", true) - if err != nil { - t.Errorf("Failed to insert true value:", err) - return - } - - _, err = db.Exec("INSERT INTO foo(id, value) VALUES(2, ?)", false) - if err != nil { - t.Errorf("Failed to insert false value:", err) - return - } - - rows, err := db.Query("SELECT id, value FROM foo") - if err != nil { - t.Errorf("Unable to query foo table:", err) - return - } - - for rows.Next() { - var id int - var value bool - - if err := rows.Scan(&id, &value); err != nil { - t.Errorf("Unable to scan results:", err) - continue - } - - if id == 1 && !value { - t.Errorf("Value for id 1 should be true, not false") - - } else if id == 2 && value { - t.Errorf("Value for id 2 should be false, not true") - } - } -} diff --git a/src/github.com/ziutek/mymysql/.gitignore b/src/github.com/ziutek/mymysql/.gitignore deleted file mode 100644 index d8357cb..0000000 --- a/src/github.com/ziutek/mymysql/.gitignore +++ /dev/null @@ -1,10 +0,0 @@ -*.6 -*.8 -*.a -*.o -*.so -*.out -*.go~ -_obj -_testmain.go -_go_.6 diff --git a/src/github.com/ziutek/mymysql/LICENSE b/src/github.com/ziutek/mymysql/LICENSE deleted file mode 100644 index 8eab9a6..0000000 --- a/src/github.com/ziutek/mymysql/LICENSE +++ /dev/null @@ -1,24 +0,0 @@ -Copyright (c) 2010, Michal Derkacz -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: -1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. -2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. -3. The name of the author may not be used to endorse or promote products - derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR -IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT -NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/src/github.com/ziutek/mymysql/Makefile b/src/github.com/ziutek/mymysql/Makefile deleted file mode 100644 index 002e3d0..0000000 --- a/src/github.com/ziutek/mymysql/Makefile +++ /dev/null @@ -1,13 +0,0 @@ -install: - ./make.bash install - -clean: - ./make.bash clean - -test: - ./make.bash test - -all: - ./make.bash clean install test - -.PHONY : install clean test all diff --git a/src/github.com/ziutek/mymysql/README.md b/src/github.com/ziutek/mymysql/README.md deleted file mode 100644 index 1796eb4..0000000 --- a/src/github.com/ziutek/mymysql/README.md +++ /dev/null @@ -1,591 +0,0 @@ -Sorry for my poor English. If you can help in improving English in this -documentation, please contact me. - -## MyMySQL v0.4 (2011-11-22) - -This package contains MySQL client API written entirely in Go. It was created -due to lack of properly working MySQL client API package, ready for my -production application (December 2010). - -This package works with the MySQL protocol version 4.1 or greater. It definitely -works well with MySQL 5.0 and 5.1 (I use these versions of MySQL for my -production application). - -The package includes an extensive set of automated tests that ensure that any -code changes during development will not break the package itself. - -## Changelog - -#### v0.4 - -1. Modular design: - - MySQL wire protocol handling moved to *mymysql/native* - - Thread safe wrapper of *native* engine in separate *mymysql/thrsafe* - - *mymysql/mysql* package contains definitions of interfaces to engines and - common (engine-independent) functions. - - Automatic reconnect interface moved to *mymysql/autorc*. - -2. *mysql.New* and other functions returns mostly interface types. So all -previously exported members were converted to methods (with except *mysql.Row* -and *mysql.Field* - they deffinition didn't changed). - -3. Transactions added. If you use *mymysql/thrsafe" engine transactions are -full thread safe. - -4. Driver for *exp/sql*. - -#### v0.3.8 - -1. Package name changed to *mysql*. -2. Connection handler name changed from *MySQL* to *Conn*. -3. Tested with Go weekly.2011-10-06 - -I think it is more pleasant to write *mysql.Conn* insted of *mymysql.MySQL*. - -#### v0.3.7 - -Works with Go release.r57.1 - -#### v0.3.6 - -The *EscapeString* method was added. - -#### v0.3.5 - -1. *IsConnected()* method was added. -2. Package name was changed from *mymy* to *mymysql*. Now the -package name corresponds to the name of Github repository. - -#### v0.3.4 - -float type disappeared because Go release.2011-01-20. If you use -older Go release use mymysql v0.3.3 - -#### v0.3.3 - -1. *Time* and *Date* types added. -2. *Run*, *Exec* and *ExecAC* accept parameters, *Start*, *Query*, -*QueryAC* no longer accept prepared statement as first argument. - -#### v0.3.2 - -1. *Register* method was added. It allows to register commands which will be -executed immediately after connect. It is mainly useful with -*Reconnect* method and autoreconn interface. -2. Multi statements / multi results were added. -3. Types *ENUM* and *SET* were added for prepared statements results. - -#### v0.3.1 - -1. There is one change in v0.3, which doesn't preserve backwards compatibility -with v0.2: the name of *Execute* method was changed to *Run*. A new *Exec* -method was added. It is similar in result to *Query* method. -2. *Reconnect* method was added. After reconnect it re-prepare all prepared -statements, related to database handler that was reconnected. -3. Autoreconn interface was added. It allows not worry about making the -connection, and not wory about re-establish connection after network error or -MySQL server restart. It is certainly safe to use it with *select* queries and -to prepare statements. You must be careful with *insert* queries. I'm not sure -whether the server performs an insert: immediately after receive query or after successfull sending OK packet. Even if it is the second option, server may not -immediately notice the network failure, becouse of network buffers in kernel. -Therefore query repetitions may cause additional unnecessary inserts into -database. This interface does not appear to be useful with local transactions. - - -## Installing - -### Using *goinstall* - preferred way: - -To install all subpackages of *mymysql* you need to goinstal three of them: - - $ goinstall github.com/ziutek/mymysql/thrsafe - $ goinstall github.com/ziutek/mymysql/autorc - $ goinstall github.com/ziutek/mymysql/godrv - -*goinstall* automagicly select proper version of *mymysql* for your Go release. -After this command *mymysql* is ready to use. You may find source in - - $GOROOT/src/pkg/github.com/ziutek/mymysql - -directory. - -You can use `goinstall -u -a` for update all installed packages. - -### Using *git clone* command: - - $ git clone git://github.com/ziutek/mymysql.git - $ cd mymysql - $ make install - -## Testing - -For testing you need test database and test user: - - mysql> create database test; - mysql> grant all privileges on test.* to testuser@localhost; - mysql> set password for testuser@localhost = password("TestPasswd9") - -Make sure that MySQL *max_allowed_packet* variable in *my.cnf* is equal or greater than 34M (needed to test long packets). - -The default MySQL test server address is *127.0.0.1:3306*. - -Next run tests: - - $ cd $GOROOT/src/pkg/github.com/ziutek/mymysql - $ make test - -## Examples - -### Example 1 - - package main - - import ( - "os" - "github.com/ziutek/mymysql/mysql" - _ "github.com/ziutek/mymysql/native" // Native engine - // _ "github.com/ziutek/mymysql/thrsafe" // Thread safe engine - ) - - func main() { - db := mysql.New("tcp", "", "127.0.0.1:3306", user, pass, dbname) - - err := db.Connect() - if err != nil { - panic(err) - } - - rows, res, err := db.Query("select * from X where id > %d", 20) - if err != nil { - panic(err) - } - - for _, row := range rows { - for _, col := range row { - if col == nil { - // col has NULL value - } else { - // Do something with text in col (type []byte) - } - } - // You can get specific value from a row - val1 := row[1].([]byte) - - // You can use it directly if conversion isn't needed - os.Stdout.Write(val1) - - // You can get converted value - number := row.Int(0) // Zero value - str := row.Str(1) // First value - bignum := row.MustUint(2) // Second value - - // You may get value by column name - val2 := row[res.Map("FirstColumn")].([]byte) - } - } - -If you do not want to load the entire result into memory you may use -*Start* and *GetRow* methods: - - res, err := db.Start("select * from X") - checkError(err) - - // Print fields names - for _, field := range res.Fields() { - fmt.Print(field.Name, " ") - } - fmt.Println() - - // Print all rows - for { - row, err := res.GetRow() - checkError(err) - - if row == nil { - // No more rows - break - } - - // Print all cols - for _, col := range row { - if col == nil { - fmt.Print("") - } else { - os.Stdout.Write(col.([]byte)) - } - fmt.Print(" ") - } - fmt.Println() - } - -### Example 2 - prepared statements - -You can use *Run* or *Exec* method for prepared statements: - - stmt, err := db.Prepare("insert into X values (?, ?)") - checkError(err) - - type Data struct { - Id int - Tax *float32 // nil means NULL - } - - data = new(Data) - - for { - err := getData(data) - if err == endOfData { - break - } - checkError(err) - - _, err = stmt.Run(data.Id, data.Tax) - checkError(err) - } - -*getData* is your function which retrieves data from somewhere and set *Id* and -*Tax* fields of the Data struct. In the case of *Tax* field *getData* may -assign pointer to retieved variable or nil if NULL should be stored in -database. - -If you pass parameters to *Run* or *Exec* method, data are rebinded on every -method call. It isn't efficient if statement is executed more than once. You -can bind parameters and use *Run* or *Exec* method without parameters, to avoid -these unnecessary rebinds. Warning! If you use *Bind* in multithreaded -application, you should be sure that no other thread will use *Bind* for the -same statement, until you no longer need binded parameters. - -The simplest way to bind parameters is: - - stmt.BindParams(data.Id, data.Tax) - -but you can't use it in our example, becouse parameters binded this way can't -be changed by *getData* function. You may modify bind like this: - - stmt.BindParams(&data.Id, &data.Tax) - -and now it should work properly. But in our example there is better solution: - - stmt.BindParams(data) - -If *BindParams* method has one parameter, and this parameter is a struct or -a pointer to the struct, it treats all fields of this struct as parameters and -bind them, - -This is improved part of previous example: - - data = new(Data) - stmt.BindParams(data) - - for { - err := getData(data) - if err == endOfData { - break - } - checkError(err) - - _, err = stmt.Run() - checkError(err) - } - -### Example 3 - using SendLongData in conjunction with http.Get - - _, err = db.Start("CREATE TABLE web (url VARCHAR(80), content LONGBLOB)") - checkError(err) - - ins, err := db.Prepare("INSERT INTO web VALUES (?, ?)") - checkError(err) - - var url string - - ins.BindParams(&url, []byte(nil)) // []byte(nil) for properly type binding - - for { - // Read URL from stdin - url = "" - fmt.Scanln(&url) - if len(url) == 0 { - // Stop reading if URL is blank line - break - } - - // Make connection - resp, err := http.Get(url) - checkError(err) - - // We can retrieve response directly into database because - // the resp.Body implements io.Reader. Use 8 kB buffer. - err = ins.SendLongData(1, resp.Body, 8192) - checkError(err) - - // Execute insert statement - _, err = ins.Run() - checkError(err) - } - -### Example 4 - multi statement / multi result - - res, err := db.Start("select id from M; select name from M") - checkError(err) - - // Get result from first select - for { - row, err := res.GetRow() - checkError(err) - if row == nil { - // End of first result - break - } - - // Do something with with the data - functionThatUseId(row.Int(0)) - } - - // Get result from second select - res, err = res.NextResult() - checkError(err) - if res == nil { - panic("Hmm, there is no result. Why?!") - } - for { - row, err := res.GetRow() - checkError(err) - if row == nil { - // End of second result - break - } - - // Do something with with the data - functionThatUseName(row.Str(0)) - } - -### Example 5 - transactions - - import ( - "github.com/ziutek/mymysql/mysql" - _ "github.com/ziutek/mymysql/thrsafe" // for thread safe transactions - - // [...] - - // Statements prepared before transaction begin - ins, err := db.Prepare("insert A values (?, ?)") - checkError(err) - - // Begin a new transaction - tr, err := db.Begin() - checkError(err) - - // Now db is locked, so any method that uses db and sends commands to - // MySQL server will be blocked until Commit or Rollback will be called. - - // Commands in transaction are thread safe to - go func() { - _, err = tr.Start("insert A values (1, 'jeden')") - checkError(err) - } () - _, err = tr.Start("insert A values (2, 'dwa')") - checkError(err) - - // You can't use statements prepared before transaction in usual way, - // because connection is locked by Begin method. You must bind statement - // to transaction before use it. - _, err = tr.Do(ins).Run(3, "trzy") - checkError(err) - - // For a greater number of calls - ti := tr.Do(ins) - _, err = ti.Run(4, "cztery") - checkError(err) - _, err = ti.Run(5, "pięć") - checkError(err) - - // At end you can Commit or Rollback. tr is invalidated and any use of it - // after Commit/Rollback causes panic. - tr.Commit() - -### Example 6 - autoreconn interface - - import ( - "github.com/ziutek/mymysql/autorc" - _ "github.com/ziutek/mymysql/thrsafe" // You may use native engine to - ) - - // [...] - - db := autorc.New("tcp", "", "127.0.0.1:3306", user, pass, dbname) - - // Initilisation commands. They will be executed after each connect. - db.Raw.Register("set names utf8") - - // There is no need to explicity connect to the MySQL server - rows, res, err := db.Query("SELECT * FROM R") - checkError(err) - - // Now we are connected. - - // It does not matter if connection will be interrupted during sleep, eg - // due to server reboot or network down. - time.Sleep(9e9) - - // If we can reconnect in no more than db.MaxRetries attempts this - // statement will be prepared. - sel, err := db.Prepare("SELECT name FROM R where id > ?") - checkError(err) - - // We can destroy our connection on server side - _, _, err = db.Query("kill %d", db.Raw.ThreadId()) - checkError(err) - - // But it doesn't matter - sel.Raw.BindParams(2) - rows, res, err = sel.Exec() - checkError(err) - -More examples are in *examples* directory. - -## Type mapping - -In the case of classic text queries, all variables that are sent to the MySQL -server are embded in text query. Thus you allways convert them to a string and -send embded in SQL query: - - rows, res, err := db.Query("select * from X where id > %d", id) - -After text query you always receive a text result. Mysql text result -corresponds to *[]byte* type in mymysql. It isn't *string* type due to -avoidance of unnecessary type conversions. You can allways convert *[]byte* to -*string* yourself: - - fmt.Print(string(rows[0][1].([]byte))) - -or usnig *Str* helper method: - - fmt.Print(rows[0].Str(1)) - -There are other helper methods, for data conversion like *Int* or *Uint*: - - fmt.Print(rows[0].Int(1)) - -All three above examples return value received in row 0 column 1. If you prefer -to use the column names, you can use *res.Map* which maps result field names to -corresponding indexes: - - name := res.Map["name"] - fmt.Print(rows[0].Str(name)) - -In case of prepared statements, the type mapping is slightly more complicated. -For parameters sended from the client to the server, Go/mymysql types are -mapped for MySQL protocol types as below: - - string --> MYSQL_TYPE_STRING - []byte --> MYSQL_TYPE_VAR_STRING - int8, uint8 --> MYSQL_TYPE_TINY - int16, uint16 --> MYSQL_TYPE_SHORT - int32, uint32 --> MYSQL_TYPE_LONG - int64, uint64 --> MYSQL_TYPE_LONGLONG - float32 --> MYSQL_TYPE_FLOAT - float64 --> MYSQL_TYPE_DOUBLE - *mysql.Timestamp --> MYSQL_TYPE_TIMESTAMP - *mysql.Datetime --> MYSQL_TYPE_DATETIME - *mysql.Date --> MYSQL_TYPE_DATE - *mysql.Time --> MYSQL_TYPE_TIME - mysql.Blob --> MYSQL_TYPE_BLOB - nil --> MYSQL_TYPE_NULL - -The MySQL server maps/converts them to a particular MySQL storage type. - -For received results MySQL storage types are mapped to Go/mymysql types as -below: - - TINYINT --> int8 - UNSIGNED TINYINT --> uint8 - SMALLINT --> int16 - UNSIGNED SMALLINT --> uint16 - MEDIUMINT, INT --> int32 - UNSIGNED MEDIUMINT, UNSIGNED INT --> uint32 - BIGINT --> int64 - UNSIGNED BIGINT --> uint64 - FLOAT --> float32 - DOUBLE --> float64 - TIMESTAMP, DATETIME --> *mysql.Datetime - DATE --> *mysql.Date - TIME --> *mysql.Time - YEAR --> int16 - CHAR, VARCHAR, BINARY, VARBINARY --> []byte - TEXT, TINYTEXT, MEDIUMTEXT, LONGTEX --> []byte - BLOB, TINYBLOB, MEDIUMBLOB, LONGBLOB --> []byte - DECIMAL, BIT --> []byte - SET, ENUM --> []byte - NULL --> nil - -## Big packets - -This package can send and receive MySQL data packets that are biger than 16 MB. -This means that you can receive response rows biger than 16 MB and can execute -prepared statements with parameter data biger than 16 MB without using -SendLongData method. If you want to use this feature you must set *MaxPktSize* -field in database handler to appropriate value before connect, and change -*max_allowed_packet* value in MySQL server configuration. - -## Thread safe engine - -If you import "mymysql/thrsafe" engine instead of "mymysql/native" engine all methods are thread safe, unless the description of the method says something else. - -If one thread is calling *Query* or *Exec* method, other threads will be -blocked if they call *Query*, *Start*, *Exec*, *Run* or other method which send -data to the server, until *Query*/*Exec* return in first thread. - -If one thread is calling *Start* or *Run* method, other threads will be -blocked if they call *Query*, *Start*, *Exec*, *Run* or other method which send -data to the server, until all results and all rows will be readed from -the connection in first thread. - -Multithreading was tested on my production web application. It uses *http* -package to serve dynamic web pages. *http* package creates one gorutine for any -HTTP connection. Any GET request during connection causes 4-8 select queries to -MySQL database (some of them are prepared statements). Database contains ca. -30 tables (three largest have 82k, 73k and 3k rows). There is one persistant -connection to MySQL server which is shared by all gorutines. Application is -running on dual-core machine with GOMAXPROCS=2. It was tested using *siege*: - - # siege my.httpserver.pl -c25 -d0 -t 30s - ** SIEGE 2.69 - ** Preparing 25 concurrent users for battle. - The server is now under siege... - Lifting the server siege.. done. - Transactions: 3212 hits - Availability: 100.00 % - Elapsed time: 29.83 secs - Data transferred: 3.88 MB - Response time: 0.22 secs - Transaction rate: 107.68 trans/sec - Throughput: 0.13 MB/sec - Concurrency: 23.43 - Successful transactions: 3218 - Failed transactions: 0 - Longest transaction: 9.28 - Shortest transaction: 0.01 - -## To do - -1. Transactions in auto reconnect interface. -2. Complete documentation - -## Known bugs - -Old passwords don't work: you obtain *UNK_RESULT_PKT_ERROR* if you try connect -to MySQL server as user which have old password format in *user* table. -Workaround: change password using result from MYSQL >= 4.1 *PASSWORD* function -(you can generate the old password format back using *OLD_PASSWORD* function). - -# Documentation - -[mysql](http://gopkgdoc.appspot.com/pkg/github.com/ziutek/mymysql/mysql) -[native](http://gopkgdoc.appspot.com/pkg/github.com/ziutek/mymysql/native) -[thrsafe](http://gopkgdoc.appspot.com/pkg/github.com/ziutek/mymysql/thrsafe) -[autorc](http://gopkgdoc.appspot.com/pkg/github.com/ziutek/mymysql/autorc) -[godrv](http://gopkgdoc.appspot.com/pkg/github.com/ziutek/mymysql/godrv) diff --git a/src/github.com/ziutek/mymysql/autorc/LICENSE b/src/github.com/ziutek/mymysql/autorc/LICENSE deleted file mode 100644 index 8eab9a6..0000000 --- a/src/github.com/ziutek/mymysql/autorc/LICENSE +++ /dev/null @@ -1,24 +0,0 @@ -Copyright (c) 2010, Michal Derkacz -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: -1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. -2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. -3. The name of the author may not be used to endorse or promote products - derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR -IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT -NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/src/github.com/ziutek/mymysql/autorc/Makefile b/src/github.com/ziutek/mymysql/autorc/Makefile deleted file mode 100644 index ba4e909..0000000 --- a/src/github.com/ziutek/mymysql/autorc/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -include $(GOROOT)/src/Make.inc - -TARG=github.com/ziutek/mymysql/autorc -GOFILES=\ - autorecon.go - -include $(GOROOT)/src/Make.pkg diff --git a/src/github.com/ziutek/mymysql/autorc/autorecon.go b/src/github.com/ziutek/mymysql/autorc/autorecon.go deleted file mode 100644 index f819bce..0000000 --- a/src/github.com/ziutek/mymysql/autorc/autorecon.go +++ /dev/null @@ -1,137 +0,0 @@ -// Auto reconnect interface for MyMySQL -package autorc - -import ( - "github.com/ziutek/mymysql/mysql" - "io" - "log" - "net" - "time" -) - -// Return true if error is network error or UnexpectedEOF. -func IsNetErr(err error) bool { - if err == io.ErrUnexpectedEOF { - return true - } else if _, ok := err.(*net.OpError); ok { - return true - } - return false -} - -type Conn struct { - Raw mysql.Conn - // Maximum reconnect retries. - // Default is 7 which means 1+2+3+4+5+6+7 = 28 seconds before return error. - MaxRetries int - - // Debug logging. You may change it at any time. - Debug bool -} - -func New(proto, laddr, raddr, user, passwd string, db ...string) *Conn { - return &Conn{mysql.New(proto, laddr, raddr, user, passwd, db...), 7, false} -} - -func (c *Conn) reconnectIfNetErr(nn *int, err *error) { - for *err != nil && IsNetErr(*err) && *nn <= c.MaxRetries { - if c.Debug { - log.Printf("Error: '%s' - reconnecting...", *err) - } - time.Sleep(1e9 * time.Duration(*nn)) - *err = c.Raw.Reconnect() - if c.Debug && *err != nil { - log.Println("Can't reconnect:", *err) - } - *nn++ - } -} - -func (c *Conn) connectIfNotConnected() (err error) { - if c.Raw.IsConnected() { - return - } - err = c.Raw.Connect() - nn := 0 - c.reconnectIfNetErr(&nn, &err) - return -} - -// Automatic connect/reconnect/repeat version of Use -func (c *Conn) Use(dbname string) (err error) { - if err = c.connectIfNotConnected(); err != nil { - return - } - nn := 0 - for { - if err = c.Raw.Use(dbname); err == nil { - return - } - if c.reconnectIfNetErr(&nn, &err); err != nil { - return - } - } - panic(nil) -} - -// Automatic connect/reconnect/repeat version of Query -func (c *Conn) Query(sql string, params ...interface{}) (rows []mysql.Row, res mysql.Result, err error) { - - if err = c.connectIfNotConnected(); err != nil { - return - } - nn := 0 - for { - if rows, res, err = c.Raw.Query(sql, params...); err == nil { - return - } - if c.reconnectIfNetErr(&nn, &err); err != nil { - return - } - } - panic(nil) -} - -type Stmt struct { - Raw mysql.Stmt - con *Conn -} - -// Automatic connect/reconnect/repeat version of Prepare -func (c *Conn) Prepare(sql string) (*Stmt, error) { - if err := c.connectIfNotConnected(); err != nil { - return nil, err - } - nn := 0 - for { - var ( - err error - s mysql.Stmt - ) - if s, err = c.Raw.Prepare(sql); err == nil { - return &Stmt{s, c}, nil - } - if c.reconnectIfNetErr(&nn, &err); err != nil { - return nil, err - } - } - panic(nil) -} - -// Automatic connect/reconnect/repeat version of Exec -func (s *Stmt) Exec(params ...interface{}) (rows []mysql.Row, res mysql.Result, err error) { - - if err = s.con.connectIfNotConnected(); err != nil { - return - } - nn := 0 - for { - if rows, res, err = s.Raw.Exec(params...); err == nil { - return - } - if s.con.reconnectIfNetErr(&nn, &err); err != nil { - return - } - } - panic(nil) -} diff --git a/src/github.com/ziutek/mymysql/autorc/autorecon_test.go b/src/github.com/ziutek/mymysql/autorc/autorecon_test.go deleted file mode 100644 index 2a8e472..0000000 --- a/src/github.com/ziutek/mymysql/autorc/autorecon_test.go +++ /dev/null @@ -1,103 +0,0 @@ -package autorc - -import ( - _ "github.com/ziutek/mymysql/thrsafe" - "testing" -) - -var ( - conn = []string{"tcp", "", "127.0.0.1:3306"} - user = "testuser" - passwd = "TestPasswd9" - dbname = "test" -) - -func checkErr(t *testing.T, err error, exp_err error) { - if err != exp_err { - if exp_err == nil { - t.Fatalf("Error: %v", err) - } else { - t.Fatalf("Error: %v\nExpected error: %v", err, exp_err) - } - } -} - -func TestAutoConnectReconnect(t *testing.T) { - c := New(conn[0], conn[1], conn[2], user, passwd) - c.Debug = false - - // Register initialisation commands - c.Raw.Register("set names utf8") - - // my is in unconnected state - checkErr(t, c.Use(dbname), nil) - - // Disconnect - c.Raw.Close() - - // Drop test table if exists - c.Query("drop table R") - - // Disconnect - c.Raw.Close() - - // Create table - _, _, err := c.Query( - "create table R (id int primary key, name varchar(20))", - ) - checkErr(t, err, nil) - - // Kill the connection - _, _, err = c.Query("kill %d", c.Raw.ThreadId()) - checkErr(t, err, nil) - - // Prepare insert statement - ins, err := c.Prepare("insert R values (?, ?)") - checkErr(t, err, nil) - - // Kill the connection - _, _, err = c.Query("kill %d", c.Raw.ThreadId()) - checkErr(t, err, nil) - - // Bind insert parameters - ins.Raw.Bind(1, "jeden") - // Insert into table - _, _, err = ins.Exec() - checkErr(t, err, nil) - - // Kill the connection - _, _, err = c.Query("kill %d", c.Raw.ThreadId()) - checkErr(t, err, nil) - - // Bind insert parameters - ins.Raw.Bind(2, "dwa") - // Insert into table - _, _, err = ins.Exec() - checkErr(t, err, nil) - - // Kill the connection - _, _, err = c.Query("kill %d", c.Raw.ThreadId()) - checkErr(t, err, nil) - - // Select from table - rows, res, err := c.Query("select * from R") - checkErr(t, err, nil) - id := res.Map("id") - name := res.Map("name") - if len(rows) != 2 || - rows[0].Int(id) != 1 || rows[0].Str(name) != "jeden" || - rows[1].Int(id) != 2 || rows[1].Str(name) != "dwa" { - t.Fatal("Bad result") - } - - // Kill the connection - _, _, err = c.Query("kill %d", c.Raw.ThreadId()) - checkErr(t, err, nil) - - // Drop table - _, _, err = c.Query("drop table R") - checkErr(t, err, nil) - - // Disconnect - c.Raw.Close() -} diff --git a/src/github.com/ziutek/mymysql/godrv/Makefile b/src/github.com/ziutek/mymysql/godrv/Makefile deleted file mode 100644 index 3209ab7..0000000 --- a/src/github.com/ziutek/mymysql/godrv/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -include $(GOROOT)/src/Make.inc - -TARG=github.com/ziutek/mymysql/godrv -GOFILES=\ - driver.go\ - -include $(GOROOT)/src/Make.pkg diff --git a/src/github.com/ziutek/mymysql/godrv/driver.go b/src/github.com/ziutek/mymysql/godrv/driver.go deleted file mode 100644 index 9b47eb6..0000000 --- a/src/github.com/ziutek/mymysql/godrv/driver.go +++ /dev/null @@ -1,217 +0,0 @@ -//MySQL driver for Go sql package -package godrv - -import ( - "database/sql" - "database/sql/driver" - "errors" - "fmt" - "github.com/ziutek/mymysql/mysql" - "github.com/ziutek/mymysql/native" - "io" - "math" - "reflect" - "strings" - "time" -) - -type conn struct { - my mysql.Conn -} - -func (c conn) Prepare(query string) (driver.Stmt, error) { - st, err := c.my.Prepare(query) - if err != nil { - return nil, err - } - return stmt{st}, nil -} - -func (c conn) Close() error { - err := c.my.Close() - c.my = nil - return err -} - -func (c conn) Begin() (driver.Tx, error) { - t, err := c.my.Begin() - if err != nil { - return tx{nil}, err - } - return tx{t}, nil -} - -type tx struct { - my mysql.Transaction -} - -func (t tx) Commit() error { - return t.my.Commit() -} - -func (t tx) Rollback() error { - return t.my.Rollback() -} - -type stmt struct { - my mysql.Stmt -} - -func (s stmt) Close() error { - err := s.my.Delete() - s.my = nil - return err -} - -func (s stmt) NumInput() int { - return s.my.NumParam() -} - -func (s stmt) run(vargs []driver.Value) (rowsRes, error) { - args := make([]interface{}, len(vargs)) - for i, a := range vargs { - args[i] = a - } - res, err := s.my.Run(args...) - if err != nil { - return rowsRes{nil}, err - } - return rowsRes{res}, nil -} - -func (s stmt) Exec(args []driver.Value) (driver.Result, error) { - return s.run(args) -} - -func (s stmt) Query(args []driver.Value) (driver.Rows, error) { - return s.run(args) -} - -type rowsRes struct { - my mysql.Result -} - -func (r rowsRes) LastInsertId() (int64, error) { - return int64(r.my.InsertId()), nil -} - -func (r rowsRes) RowsAffected() (int64, error) { - return int64(r.my.AffectedRows()), nil -} - -func (r rowsRes) Columns() []string { - flds := r.my.Fields() - cls := make([]string, len(flds)) - for i, f := range flds { - cls[i] = f.Name - } - return cls -} - -func (r rowsRes) Close() error { - err := r.my.End() - r.my = nil - if err != native.READ_AFTER_EOR_ERROR { - return err - } - return nil -} - -// DATE, DATETIME, TIMESTAMP are treated as they are in Local time zone -func (r rowsRes) Next(dest []driver.Value) error { - row, err := r.my.GetRow() - if err != nil { - return err - } - if row == nil { - return io.EOF - } - for i, col := range row { - if col == nil { - dest[i] = nil - continue - } - switch c := col.(type) { - case time.Time: - dest[i] = c - continue - case mysql.Timestamp: - dest[i] = c.Time - continue - case mysql.Date: - dest[i] = c.Localtime() - continue - } - v := reflect.ValueOf(col) - switch v.Kind() { - case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - // this contains time.Duration to - dest[i] = v.Int() - case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: - u := v.Uint() - if u > math.MaxInt64 { - panic("Value to large for int64 type") - } - dest[i] = int64(u) - case reflect.Float32, reflect.Float64: - dest[i] = v.Float() - case reflect.Slice: - if v.Type().Elem().Kind() == reflect.Uint8 { - dest[i] = v.Interface().([]byte) - break - } - fallthrough - default: - panic(fmt.Sprint("Unknown type of column: ", v.Type())) - } - } - return nil -} - -type Driver struct { - // Defaults - proto, laddr, raddr, user, passwd, db string -} - -// Open new connection. The uri need to have the following syntax: -// -// [PROTOCOL_SPECFIIC*]DBNAME/USER/PASSWD -// -// where protocol spercific part may be empty (this means connection to -// local server using default protocol). Currently possible forms: -// DBNAME/USER/PASSWD -// unix:SOCKPATH*DBNAME/USER/PASSWD -// tcp:ADDR*DBNAME/USER/PASSWD -func (d *Driver) Open(uri string) (driver.Conn, error) { - pd := strings.SplitN(uri, "*", 2) - if len(pd) == 2 { - // Parse protocol part of URI - p := strings.SplitN(pd[0], ":", 2) - if len(p) != 2 { - return nil, errors.New("Wrong protocol part of URI") - } - d.proto = p[0] - d.raddr = p[1] - // Remove protocol part - pd = pd[1:] - } - // Parse database part of URI - dup := strings.SplitN(pd[0], "/", 3) - if len(dup) != 3 { - return nil, errors.New("Wrong database part of URI") - } - d.db = dup[0] - d.user = dup[1] - d.passwd = dup[2] - - // Establish the connection - c := conn{mysql.New(d.proto, d.laddr, d.raddr, d.user, d.passwd, d.db)} - if err := c.my.Connect(); err != nil { - return nil, err - } - return &c, nil -} - -func init() { - sql.Register("mymysql", &Driver{proto: "tcp", raddr: "127.0.0.1:3306"}) -} diff --git a/src/github.com/ziutek/mymysql/godrv/driver_test.go b/src/github.com/ziutek/mymysql/godrv/driver_test.go deleted file mode 100644 index 4c5a0d8..0000000 --- a/src/github.com/ziutek/mymysql/godrv/driver_test.go +++ /dev/null @@ -1,75 +0,0 @@ -package godrv - -import ( - "database/sql" - "testing" -) - -func checkErr(t *testing.T, err error) { - if err != nil { - t.Fatalf("Error: %v", err) - } -} -func checkErrId(t *testing.T, err error, rid, eid int64) { - checkErr(t, err) - if rid != eid { - t.Fatal("res.LastInsertId() ==", rid, "but should be", eid) - } -} - -func TestAll(t *testing.T) { - data := []string{"jeden", "dwa"} - - db, err := sql.Open("mymysql", "test/testuser/TestPasswd9") - - db.Exec("DROP TABLE go") - - _, err = db.Exec( - `CREATE TABLE go ( - id INT PRIMARY KEY AUTO_INCREMENT, - txt TEXT - ) ENGINE=InnoDB`) - checkErr(t, err) - - ins, err := db.Prepare("INSERT go SET txt=?") - checkErr(t, err) - - tx, err := db.Begin() - checkErr(t, err) - - res, err := ins.Exec(data[0]) - checkErr(t, err) - id, err := res.LastInsertId() - checkErrId(t, err, id, 1) - - res, err = ins.Exec(data[1]) - checkErr(t, err) - id, err = res.LastInsertId() - checkErrId(t, err, id, 2) - - checkErr(t, tx.Commit()) - - tx, err = db.Begin() - checkErr(t, err) - - res, err = tx.Exec("INSERT go SET txt=?", "trzy") - checkErr(t, err) - id, err = res.LastInsertId() - checkErrId(t, err, id, 3) - - checkErr(t, tx.Rollback()) - - rows, err := db.Query("SELECT * FROM go") - checkErr(t, err) - for rows.Next() { - var id int - var txt string - checkErr(t, rows.Scan(&id, &txt)) - if id > len(data) { - t.Fatal("To many rows in table") - } - if data[id-1] != txt { - t.Fatalf("txt[%d] == '%s' != '%s'", id, txt, data[id-1]) - } - } -} diff --git a/src/github.com/ziutek/mymysql/make.bash b/src/github.com/ziutek/mymysql/make.bash deleted file mode 100755 index 95ecf42..0000000 --- a/src/github.com/ziutek/mymysql/make.bash +++ /dev/null @@ -1,5 +0,0 @@ -#!/usr/bin/env bash - -for dir in mysql native thrsafe autorc godrv examples; do - (cd $dir; make $@) -done diff --git a/src/github.com/ziutek/mymysql/mysql/Makefile b/src/github.com/ziutek/mymysql/mysql/Makefile deleted file mode 100644 index c4f8a66..0000000 --- a/src/github.com/ziutek/mymysql/mysql/Makefile +++ /dev/null @@ -1,12 +0,0 @@ -include $(GOROOT)/src/Make.inc - -TARG=github.com/ziutek/mymysql/mysql -GOFILES=\ - types.go\ - field.go\ - row.go\ - interface.go\ - utils.go\ - errors.go\ - -include $(GOROOT)/src/Make.pkg diff --git a/src/github.com/ziutek/mymysql/mysql/errors.go b/src/github.com/ziutek/mymysql/mysql/errors.go deleted file mode 100644 index 8946f86..0000000 --- a/src/github.com/ziutek/mymysql/mysql/errors.go +++ /dev/null @@ -1,494 +0,0 @@ -package mysql - -import ( - "fmt" -) - -// If function/method returns error you can check returned error type, and if -// it is *mymy.Error it is error received from MySQL server. Next you can check -// Code for error number. -type Error struct { - Code uint16 - Msg []byte -} - -func (err Error) Error() string { - return fmt.Sprintf("Received #%d error from MySQL server: \"%s\"", - err.Code, err.Msg) -} - -// MySQL error codes -const ( - ER_HASHCHK = 1000 - ER_NISAMCHK = 1001 - ER_NO = 1002 - ER_YES = 1003 - ER_CANT_CREATE_FILE = 1004 - ER_CANT_CREATE_TABLE = 1005 - ER_CANT_CREATE_DB = 1006 - ER_DB_CREATE_EXISTS = 1007 - ER_DB_DROP_EXISTS = 1008 - ER_DB_DROP_DELETE = 1009 - ER_DB_DROP_RMDIR = 1010 - ER_CANT_DELETE_FILE = 1011 - ER_CANT_FIND_SYSTEM_REC = 1012 - ER_CANT_GET_STAT = 1013 - ER_CANT_GET_WD = 1014 - ER_CANT_LOCK = 1015 - ER_CANT_OPEN_FILE = 1016 - ER_FILE_NOT_FOUND = 1017 - ER_CANT_READ_DIR = 1018 - ER_CANT_SET_WD = 1019 - ER_CHECKREAD = 1020 - ER_DISK_FULL = 1021 - ER_DUP_KEY = 1022 - ER_ERROR_ON_CLOSE = 1023 - ER_ERROR_ON_READ = 1024 - ER_ERROR_ON_RENAME = 1025 - ER_ERROR_ON_WRITE = 1026 - ER_FILE_USED = 1027 - ER_FILSORT_ABORT = 1028 - ER_FORM_NOT_FOUND = 1029 - ER_GET_ERRNO = 1030 - ER_ILLEGAL_HA = 1031 - ER_KEY_NOT_FOUND = 1032 - ER_NOT_FORM_FILE = 1033 - ER_NOT_KEYFILE = 1034 - ER_OLD_KEYFILE = 1035 - ER_OPEN_AS_READONLY = 1036 - ER_OUTOFMEMORY = 1037 - ER_OUT_OF_SORTMEMORY = 1038 - ER_UNEXPECTED_EOF = 1039 - ER_CON_COUNT_ERROR = 1040 - ER_OUT_OF_RESOURCES = 1041 - ER_BAD_HOST_ERROR = 1042 - ER_HANDSHAKE_ERROR = 1043 - ER_DBACCESS_DENIED_ERROR = 1044 - ER_ACCESS_DENIED_ERROR = 1045 - ER_NO_DB_ERROR = 1046 - ER_UNKNOWN_COM_ERROR = 1047 - ER_BAD_NULL_ERROR = 1048 - ER_BAD_DB_ERROR = 1049 - ER_TABLE_EXISTS_ERROR = 1050 - ER_BAD_TABLE_ERROR = 1051 - ER_NON_UNIQ_ERROR = 1052 - ER_SERVER_SHUTDOWN = 1053 - ER_BAD_FIELD_ERROR = 1054 - ER_WRONG_FIELD_WITH_GROUP = 1055 - ER_WRONG_GROUP_FIELD = 1056 - ER_WRONG_SUM_SELECT = 1057 - ER_WRONG_VALUE_COUNT = 1058 - ER_TOO_LONG_IDENT = 1059 - ER_DUP_FIELDNAME = 1060 - ER_DUP_KEYNAME = 1061 - ER_DUP_ENTRY = 1062 - ER_WRONG_FIELD_SPEC = 1063 - ER_PARSE_ERROR = 1064 - ER_EMPTY_QUERY = 1065 - ER_NONUNIQ_TABLE = 1066 - ER_INVALID_DEFAULT = 1067 - ER_MULTIPLE_PRI_KEY = 1068 - ER_TOO_MANY_KEYS = 1069 - ER_TOO_MANY_KEY_PARTS = 1070 - ER_TOO_LONG_KEY = 1071 - ER_KEY_COLUMN_DOES_NOT_EXITS = 1072 - ER_BLOB_USED_AS_KEY = 1073 - ER_TOO_BIG_FIELDLENGTH = 1074 - ER_WRONG_AUTO_KEY = 1075 - ER_READY = 1076 - ER_NORMAL_SHUTDOWN = 1077 - ER_GOT_SIGNAL = 1078 - ER_SHUTDOWN_COMPLETE = 1079 - ER_FORCING_CLOSE = 1080 - ER_IPSOCK_ERROR = 1081 - ER_NO_SUCH_INDEX = 1082 - ER_WRONG_FIELD_TERMINATORS = 1083 - ER_BLOBS_AND_NO_TERMINATED = 1084 - ER_TEXTFILE_NOT_READABLE = 1085 - ER_FILE_EXISTS_ERROR = 1086 - ER_LOAD_INFO = 1087 - ER_ALTER_INFO = 1088 - ER_WRONG_SUB_KEY = 1089 - ER_CANT_REMOVE_ALL_FIELDS = 1090 - ER_CANT_DROP_FIELD_OR_KEY = 1091 - ER_INSERT_INFO = 1092 - ER_UPDATE_TABLE_USED = 1093 - ER_NO_SUCH_THREAD = 1094 - ER_KILL_DENIED_ERROR = 1095 - ER_NO_TABLES_USED = 1096 - ER_TOO_BIG_SET = 1097 - ER_NO_UNIQUE_LOGFILE = 1098 - ER_TABLE_NOT_LOCKED_FOR_WRITE = 1099 - ER_TABLE_NOT_LOCKED = 1100 - ER_BLOB_CANT_HAVE_DEFAULT = 1101 - ER_WRONG_DB_NAME = 1102 - ER_WRONG_TABLE_NAME = 1103 - ER_TOO_BIG_SELECT = 1104 - ER_UNKNOWN_ERROR = 1105 - ER_UNKNOWN_PROCEDURE = 1106 - ER_WRONG_PARAMCOUNT_TO_PROCEDURE = 1107 - ER_WRONG_PARAMETERS_TO_PROCEDURE = 1108 - ER_UNKNOWN_TABLE = 1109 - ER_FIELD_SPECIFIED_TWICE = 1110 - ER_INVALID_GROUP_FUNC_USE = 1111 - ER_UNSUPPORTED_EXTENSION = 1112 - ER_TABLE_MUST_HAVE_COLUMNS = 1113 - ER_RECORD_FILE_FULL = 1114 - ER_UNKNOWN_CHARACTER_SET = 1115 - ER_TOO_MANY_TABLES = 1116 - ER_TOO_MANY_FIELDS = 1117 - ER_TOO_BIG_ROWSIZE = 1118 - ER_STACK_OVERRUN = 1119 - ER_WRONG_OUTER_JOIN = 1120 - ER_NULL_COLUMN_IN_INDEX = 1121 - ER_CANT_FIND_UDF = 1122 - ER_CANT_INITIALIZE_UDF = 1123 - ER_UDF_NO_PATHS = 1124 - ER_UDF_EXISTS = 1125 - ER_CANT_OPEN_LIBRARY = 1126 - ER_CANT_FIND_DL_ENTRY = 1127 - ER_FUNCTION_NOT_DEFINED = 1128 - ER_HOST_IS_BLOCKED = 1129 - ER_HOST_NOT_PRIVILEGED = 1130 - ER_PASSWORD_ANONYMOUS_USER = 1131 - ER_PASSWORD_NOT_ALLOWED = 1132 - ER_PASSWORD_NO_MATCH = 1133 - ER_UPDATE_INFO = 1134 - ER_CANT_CREATE_THREAD = 1135 - ER_WRONG_VALUE_COUNT_ON_ROW = 1136 - ER_CANT_REOPEN_TABLE = 1137 - ER_INVALID_USE_OF_NULL = 1138 - ER_REGEXP_ERROR = 1139 - ER_MIX_OF_GROUP_FUNC_AND_FIELDS = 1140 - ER_NONEXISTING_GRANT = 1141 - ER_TABLEACCESS_DENIED_ERROR = 1142 - ER_COLUMNACCESS_DENIED_ERROR = 1143 - ER_ILLEGAL_GRANT_FOR_TABLE = 1144 - ER_GRANT_WRONG_HOST_OR_USER = 1145 - ER_NO_SUCH_TABLE = 1146 - ER_NONEXISTING_TABLE_GRANT = 1147 - ER_NOT_ALLOWED_COMMAND = 1148 - ER_SYNTAX_ERROR = 1149 - ER_DELAYED_CANT_CHANGE_LOCK = 1150 - ER_TOO_MANY_DELAYED_THREADS = 1151 - ER_ABORTING_CONNECTION = 1152 - ER_NET_PACKET_TOO_LARGE = 1153 - ER_NET_READ_ERROR_FROM_PIPE = 1154 - ER_NET_FCNTL_ERROR = 1155 - ER_NET_PACKETS_OUT_OF_ORDER = 1156 - ER_NET_UNCOMPRESS_ERROR = 1157 - ER_NET_READ_ERROR = 1158 - ER_NET_READ_INTERRUPTED = 1159 - ER_NET_ERROR_ON_WRITE = 1160 - ER_NET_WRITE_INTERRUPTED = 1161 - ER_TOO_LONG_STRING = 1162 - ER_TABLE_CANT_HANDLE_BLOB = 1163 - ER_TABLE_CANT_HANDLE_AUTO_INCREMENT = 1164 - ER_DELAYED_INSERT_TABLE_LOCKED = 1165 - ER_WRONG_COLUMN_NAME = 1166 - ER_WRONG_KEY_COLUMN = 1167 - ER_WRONG_MRG_TABLE = 1168 - ER_DUP_UNIQUE = 1169 - ER_BLOB_KEY_WITHOUT_LENGTH = 1170 - ER_PRIMARY_CANT_HAVE_NULL = 1171 - ER_TOO_MANY_ROWS = 1172 - ER_REQUIRES_PRIMARY_KEY = 1173 - ER_NO_RAID_COMPILED = 1174 - ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE = 1175 - ER_KEY_DOES_NOT_EXITS = 1176 - ER_CHECK_NO_SUCH_TABLE = 1177 - ER_CHECK_NOT_IMPLEMENTED = 1178 - ER_CANT_DO_THIS_DURING_AN_TRANSACTION = 1179 - ER_ERROR_DURING_COMMIT = 1180 - ER_ERROR_DURING_ROLLBACK = 1181 - ER_ERROR_DURING_FLUSH_LOGS = 1182 - ER_ERROR_DURING_CHECKPOINT = 1183 - ER_NEW_ABORTING_CONNECTION = 1184 - ER_DUMP_NOT_IMPLEMENTED = 1185 - ER_FLUSH_MASTER_BINLOG_CLOSED = 1186 - ER_INDEX_REBUILD = 1187 - ER_MASTER = 1188 - ER_MASTER_NET_READ = 1189 - ER_MASTER_NET_WRITE = 1190 - ER_FT_MATCHING_KEY_NOT_FOUND = 1191 - ER_LOCK_OR_ACTIVE_TRANSACTION = 1192 - ER_UNKNOWN_SYSTEM_VARIABLE = 1193 - ER_CRASHED_ON_USAGE = 1194 - ER_CRASHED_ON_REPAIR = 1195 - ER_WARNING_NOT_COMPLETE_ROLLBACK = 1196 - ER_TRANS_CACHE_FULL = 1197 - ER_SLAVE_MUST_STOP = 1198 - ER_SLAVE_NOT_RUNNING = 1199 - ER_BAD_SLAVE = 1200 - ER_MASTER_INFO = 1201 - ER_SLAVE_THREAD = 1202 - ER_TOO_MANY_USER_CONNECTIONS = 1203 - ER_SET_CONSTANTS_ONLY = 1204 - ER_LOCK_WAIT_TIMEOUT = 1205 - ER_LOCK_TABLE_FULL = 1206 - ER_READ_ONLY_TRANSACTION = 1207 - ER_DROP_DB_WITH_READ_LOCK = 1208 - ER_CREATE_DB_WITH_READ_LOCK = 1209 - ER_WRONG_ARGUMENTS = 1210 - ER_NO_PERMISSION_TO_CREATE_USER = 1211 - ER_UNION_TABLES_IN_DIFFERENT_DIR = 1212 - ER_LOCK_DEADLOCK = 1213 - ER_TABLE_CANT_HANDLE_FT = 1214 - ER_CANNOT_ADD_FOREIGN = 1215 - ER_NO_REFERENCED_ROW = 1216 - ER_ROW_IS_REFERENCED = 1217 - ER_CONNECT_TO_MASTER = 1218 - ER_QUERY_ON_MASTER = 1219 - ER_ERROR_WHEN_EXECUTING_COMMAND = 1220 - ER_WRONG_USAGE = 1221 - ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT = 1222 - ER_CANT_UPDATE_WITH_READLOCK = 1223 - ER_MIXING_NOT_ALLOWED = 1224 - ER_DUP_ARGUMENT = 1225 - ER_USER_LIMIT_REACHED = 1226 - ER_SPECIFIC_ACCESS_DENIED_ERROR = 1227 - ER_LOCAL_VARIABLE = 1228 - ER_GLOBAL_VARIABLE = 1229 - ER_NO_DEFAULT = 1230 - ER_WRONG_VALUE_FOR_VAR = 1231 - ER_WRONG_TYPE_FOR_VAR = 1232 - ER_VAR_CANT_BE_READ = 1233 - ER_CANT_USE_OPTION_HERE = 1234 - ER_NOT_SUPPORTED_YET = 1235 - ER_MASTER_FATAL_ERROR_READING_BINLOG = 1236 - ER_SLAVE_IGNORED_TABLE = 1237 - ER_INCORRECT_GLOBAL_LOCAL_VAR = 1238 - ER_WRONG_FK_DEF = 1239 - ER_KEY_REF_DO_NOT_MATCH_TABLE_REF = 1240 - ER_OPERAND_COLUMNS = 1241 - ER_SUBQUERY_NO_1_ROW = 1242 - ER_UNKNOWN_STMT_HANDLER = 1243 - ER_CORRUPT_HELP_DB = 1244 - ER_CYCLIC_REFERENCE = 1245 - ER_AUTO_CONVERT = 1246 - ER_ILLEGAL_REFERENCE = 1247 - ER_DERIVED_MUST_HAVE_ALIAS = 1248 - ER_SELECT_REDUCED = 1249 - ER_TABLENAME_NOT_ALLOWED_HERE = 1250 - ER_NOT_SUPPORTED_AUTH_MODE = 1251 - ER_SPATIAL_CANT_HAVE_NULL = 1252 - ER_COLLATION_CHARSET_MISMATCH = 1253 - ER_SLAVE_WAS_RUNNING = 1254 - ER_SLAVE_WAS_NOT_RUNNING = 1255 - ER_TOO_BIG_FOR_UNCOMPRESS = 1256 - ER_ZLIB_Z_MEM_ERROR = 1257 - ER_ZLIB_Z_BUF_ERROR = 1258 - ER_ZLIB_Z_DATA_ERROR = 1259 - ER_CUT_VALUE_GROUP_CONCAT = 1260 - ER_WARN_TOO_FEW_RECORDS = 1261 - ER_WARN_TOO_MANY_RECORDS = 1262 - ER_WARN_NULL_TO_NOTNULL = 1263 - ER_WARN_DATA_OUT_OF_RANGE = 1264 - WARN_DATA_TRUNCATED = 1265 - ER_WARN_USING_OTHER_HANDLER = 1266 - ER_CANT_AGGREGATE_2COLLATIONS = 1267 - ER_DROP_USER = 1268 - ER_REVOKE_GRANTS = 1269 - ER_CANT_AGGREGATE_3COLLATIONS = 1270 - ER_CANT_AGGREGATE_NCOLLATIONS = 1271 - ER_VARIABLE_IS_NOT_STRUCT = 1272 - ER_UNKNOWN_COLLATION = 1273 - ER_SLAVE_IGNORED_SSL_PARAMS = 1274 - ER_SERVER_IS_IN_SECURE_AUTH_MODE = 1275 - ER_WARN_FIELD_RESOLVED = 1276 - ER_BAD_SLAVE_UNTIL_COND = 1277 - ER_MISSING_SKIP_SLAVE = 1278 - ER_UNTIL_COND_IGNORED = 1279 - ER_WRONG_NAME_FOR_INDEX = 1280 - ER_WRONG_NAME_FOR_CATALOG = 1281 - ER_WARN_QC_RESIZE = 1282 - ER_BAD_FT_COLUMN = 1283 - ER_UNKNOWN_KEY_CACHE = 1284 - ER_WARN_HOSTNAME_WONT_WORK = 1285 - ER_UNKNOWN_STORAGE_ENGINE = 1286 - ER_WARN_DEPRECATED_SYNTAX = 1287 - ER_NON_UPDATABLE_TABLE = 1288 - ER_FEATURE_DISABLED = 1289 - ER_OPTION_PREVENTS_STATEMENT = 1290 - ER_DUPLICATED_VALUE_IN_TYPE = 1291 - ER_TRUNCATED_WRONG_VALUE = 1292 - ER_TOO_MUCH_AUTO_TIMESTAMP_COLS = 1293 - ER_INVALID_ON_UPDATE = 1294 - ER_UNSUPPORTED_PS = 1295 - ER_GET_ERRMSG = 1296 - ER_GET_TEMPORARY_ERRMSG = 1297 - ER_UNKNOWN_TIME_ZONE = 1298 - ER_WARN_INVALID_TIMESTAMP = 1299 - ER_INVALID_CHARACTER_STRING = 1300 - ER_WARN_ALLOWED_PACKET_OVERFLOWED = 1301 - ER_CONFLICTING_DECLARATIONS = 1302 - ER_SP_NO_RECURSIVE_CREATE = 1303 - ER_SP_ALREADY_EXISTS = 1304 - ER_SP_DOES_NOT_EXIST = 1305 - ER_SP_DROP_FAILED = 1306 - ER_SP_STORE_FAILED = 1307 - ER_SP_LILABEL_MISMATCH = 1308 - ER_SP_LABEL_REDEFINE = 1309 - ER_SP_LABEL_MISMATCH = 1310 - ER_SP_UNINIT_VAR = 1311 - ER_SP_BADSELECT = 1312 - ER_SP_BADRETURN = 1313 - ER_SP_BADSTATEMENT = 1314 - ER_UPDATE_LOG_DEPRECATED_IGNORED = 1315 - ER_UPDATE_LOG_DEPRECATED_TRANSLATED = 1316 - ER_QUERY_INTERRUPTED = 1317 - ER_SP_WRONG_NO_OF_ARGS = 1318 - ER_SP_COND_MISMATCH = 1319 - ER_SP_NORETURN = 1320 - ER_SP_NORETURNEND = 1321 - ER_SP_BAD_CURSOR_QUERY = 1322 - ER_SP_BAD_CURSOR_SELECT = 1323 - ER_SP_CURSOR_MISMATCH = 1324 - ER_SP_CURSOR_ALREADY_OPEN = 1325 - ER_SP_CURSOR_NOT_OPEN = 1326 - ER_SP_UNDECLARED_VAR = 1327 - ER_SP_WRONG_NO_OF_FETCH_ARGS = 1328 - ER_SP_FETCH_NO_DATA = 1329 - ER_SP_DUP_PARAM = 1330 - ER_SP_DUP_VAR = 1331 - ER_SP_DUP_COND = 1332 - ER_SP_DUP_CURS = 1333 - ER_SP_CANT_ALTER = 1334 - ER_SP_SUBSELECT_NYI = 1335 - ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG = 1336 - ER_SP_VARCOND_AFTER_CURSHNDLR = 1337 - ER_SP_CURSOR_AFTER_HANDLER = 1338 - ER_SP_CASE_NOT_FOUND = 1339 - ER_FPARSER_TOO_BIG_FILE = 1340 - ER_FPARSER_BAD_HEADER = 1341 - ER_FPARSER_EOF_IN_COMMENT = 1342 - ER_FPARSER_ERROR_IN_PARAMETER = 1343 - ER_FPARSER_EOF_IN_UNKNOWN_PARAMETER = 1344 - ER_VIEW_NO_EXPLAIN = 1345 - ER_FRM_UNKNOWN_TYPE = 1346 - ER_WRONG_OBJECT = 1347 - ER_NONUPDATEABLE_COLUMN = 1348 - ER_VIEW_SELECT_DERIVED = 1349 - ER_VIEW_SELECT_CLAUSE = 1350 - ER_VIEW_SELECT_VARIABLE = 1351 - ER_VIEW_SELECT_TMPTABLE = 1352 - ER_VIEW_WRONG_LIST = 1353 - ER_WARN_VIEW_MERGE = 1354 - ER_WARN_VIEW_WITHOUT_KEY = 1355 - ER_VIEW_INVALID = 1356 - ER_SP_NO_DROP_SP = 1357 - ER_SP_GOTO_IN_HNDLR = 1358 - ER_TRG_ALREADY_EXISTS = 1359 - ER_TRG_DOES_NOT_EXIST = 1360 - ER_TRG_ON_VIEW_OR_TEMP_TABLE = 1361 - ER_TRG_CANT_CHANGE_ROW = 1362 - ER_TRG_NO_SUCH_ROW_IN_TRG = 1363 - ER_NO_DEFAULT_FOR_FIELD = 1364 - ER_DIVISION_BY_ZERO = 1365 - ER_TRUNCATED_WRONG_VALUE_FOR_FIELD = 1366 - ER_ILLEGAL_VALUE_FOR_TYPE = 1367 - ER_VIEW_NONUPD_CHECK = 1368 - ER_VIEW_CHECK_FAILED = 1369 - ER_PROCACCESS_DENIED_ERROR = 1370 - ER_RELAY_LOG_FAIL = 1371 - ER_PASSWD_LENGTH = 1372 - ER_UNKNOWN_TARGET_BINLOG = 1373 - ER_IO_ERR_LOG_INDEX_READ = 1374 - ER_BINLOG_PURGE_PROHIBITED = 1375 - ER_FSEEK_FAIL = 1376 - ER_BINLOG_PURGE_FATAL_ERR = 1377 - ER_LOG_IN_USE = 1378 - ER_LOG_PURGE_UNKNOWN_ERR = 1379 - ER_RELAY_LOG_INIT = 1380 - ER_NO_BINARY_LOGGING = 1381 - ER_RESERVED_SYNTAX = 1382 - ER_WSAS_FAILED = 1383 - ER_DIFF_GROUPS_PROC = 1384 - ER_NO_GROUP_FOR_PROC = 1385 - ER_ORDER_WITH_PROC = 1386 - ER_LOGGING_PROHIBIT_CHANGING_OF = 1387 - ER_NO_FILE_MAPPING = 1388 - ER_WRONG_MAGIC = 1389 - ER_PS_MANY_PARAM = 1390 - ER_KEY_PART_0 = 1391 - ER_VIEW_CHECKSUM = 1392 - ER_VIEW_MULTIUPDATE = 1393 - ER_VIEW_NO_INSERT_FIELD_LIST = 1394 - ER_VIEW_DELETE_MERGE_VIEW = 1395 - ER_CANNOT_USER = 1396 - ER_XAER_NOTA = 1397 - ER_XAER_INVAL = 1398 - ER_XAER_RMFAIL = 1399 - ER_XAER_OUTSIDE = 1400 - ER_XAER_RMERR = 1401 - ER_XA_RBROLLBACK = 1402 - ER_NONEXISTING_PROC_GRANT = 1403 - ER_PROC_AUTO_GRANT_FAIL = 1404 - ER_PROC_AUTO_REVOKE_FAIL = 1405 - ER_DATA_TOO_LONG = 1406 - ER_SP_BAD_SQLSTATE = 1407 - ER_STARTUP = 1408 - ER_LOAD_FROM_FIXED_SIZE_ROWS_TO_VAR = 1409 - ER_CANT_CREATE_USER_WITH_GRANT = 1410 - ER_WRONG_VALUE_FOR_TYPE = 1411 - ER_TABLE_DEF_CHANGED = 1412 - ER_SP_DUP_HANDLER = 1413 - ER_SP_NOT_VAR_ARG = 1414 - ER_SP_NO_RETSET = 1415 - ER_CANT_CREATE_GEOMETRY_OBJECT = 1416 - ER_FAILED_ROUTINE_BREAK_BINLOG = 1417 - ER_BINLOG_UNSAFE_ROUTINE = 1418 - ER_BINLOG_CREATE_ROUTINE_NEED_SUPER = 1419 - ER_EXEC_STMT_WITH_OPEN_CURSOR = 1420 - ER_STMT_HAS_NO_OPEN_CURSOR = 1421 - ER_COMMIT_NOT_ALLOWED_IN_SF_OR_TRG = 1422 - ER_NO_DEFAULT_FOR_VIEW_FIELD = 1423 - ER_SP_NO_RECURSION = 1424 - ER_TOO_BIG_SCALE = 1425 - ER_TOO_BIG_PRECISION = 1426 - ER_M_BIGGER_THAN_D = 1427 - ER_WRONG_LOCK_OF_SYSTEM_TABLE = 1428 - ER_CONNECT_TO_FOREIGN_DATA_SOURCE = 1429 - ER_QUERY_ON_FOREIGN_DATA_SOURCE = 1430 - ER_FOREIGN_DATA_SOURCE_DOESNT_EXIST = 1431 - ER_FOREIGN_DATA_STRING_INVALID_CANT_CREATE = 1432 - ER_FOREIGN_DATA_STRING_INVALID = 1433 - ER_CANT_CREATE_FEDERATED_TABLE = 1434 - ER_TRG_IN_WRONG_SCHEMA = 1435 - ER_STACK_OVERRUN_NEED_MORE = 1436 - ER_TOO_LONG_BODY = 1437 - ER_WARN_CANT_DROP_DEFAULT_KEYCACHE = 1438 - ER_TOO_BIG_DISPLAYWIDTH = 1439 - ER_XAER_DUPID = 1440 - ER_DATETIME_FUNCTION_OVERFLOW = 1441 - ER_CANT_UPDATE_USED_TABLE_IN_SF_OR_TRG = 1442 - ER_VIEW_PREVENT_UPDATE = 1443 - ER_PS_NO_RECURSION = 1444 - ER_SP_CANT_SET_AUTOCOMMIT = 1445 - ER_MALFORMED_DEFINER = 1446 - ER_VIEW_FRM_NO_USER = 1447 - ER_VIEW_OTHER_USER = 1448 - ER_NO_SUCH_USER = 1449 - ER_FORBID_SCHEMA_CHANGE = 1450 - ER_ROW_IS_REFERENCED_2 = 1451 - ER_NO_REFERENCED_ROW_2 = 1452 - ER_SP_BAD_VAR_SHADOW = 1453 - ER_TRG_NO_DEFINER = 1454 - ER_OLD_FILE_FORMAT = 1455 - ER_SP_RECURSION_LIMIT = 1456 - ER_SP_PROC_TABLE_CORRUPT = 1457 - ER_SP_WRONG_NAME = 1458 - ER_TABLE_NEEDS_UPGRADE = 1459 - ER_SP_NO_AGGREGATE = 1460 - ER_MAX_PREPARED_STMT_COUNT_REACHED = 1461 - ER_VIEW_RECURSIVE = 1462 - ER_NON_GROUPING_FIELD_USED = 1463 - ER_TABLE_CANT_HANDLE_SPKEYS = 1464 - ER_NO_TRIGGERS_ON_SYSTEM_SCHEMA = 1465 - ER_REMOVED_SPACES = 1466 - ER_AUTOINC_READ_FAILED = 1467 - ER_USERNAME = 1468 - ER_HOSTNAME = 1469 - ER_WRONG_STRING_LENGTH = 1470 - ER_NON_INSERTABLE_TABLE = 1471 -) diff --git a/src/github.com/ziutek/mymysql/mysql/field.go b/src/github.com/ziutek/mymysql/mysql/field.go deleted file mode 100644 index 7ecf1b7..0000000 --- a/src/github.com/ziutek/mymysql/mysql/field.go +++ /dev/null @@ -1,15 +0,0 @@ -package mysql - -type Field struct { - Catalog string - Db string - Table string - OrgTable string - Name string - OrgName string - DispLen uint32 - // Charset uint16 - Flags uint16 - Type byte - Scale byte -} diff --git a/src/github.com/ziutek/mymysql/mysql/interface.go b/src/github.com/ziutek/mymysql/mysql/interface.go deleted file mode 100644 index ee67975..0000000 --- a/src/github.com/ziutek/mymysql/mysql/interface.go +++ /dev/null @@ -1,69 +0,0 @@ -// MySQL Client API written entirely in Go without any external dependences. -package mysql - -type ConnCommon interface { - Start(sql string, params ...interface{}) (Result, error) - Prepare(sql string) (Stmt, error) - - Ping() error - ThreadId() uint32 - EscapeString(txt string) string - - Query(sql string, params ...interface{}) ([]Row, Result, error) -} - -type Conn interface { - ConnCommon - - Connect() error - Close() error - IsConnected() bool - Reconnect() error - Use(dbname string) error - Register(sql string) - SetMaxPktSize(new_size int) int - - Begin() (Transaction, error) -} - -type Transaction interface { - ConnCommon - - Commit() error - Rollback() error - Do(st Stmt) Stmt -} - -type Stmt interface { - Bind(params ...interface{}) - ResetParams() - Run(params ...interface{}) (Result, error) - Delete() error - Reset() error - SendLongData(pnum int, data interface{}, pkt_size int) error - - Map(string) int - NumField() int - NumParam() int - WarnCount() int - - Exec(params ...interface{}) ([]Row, Result, error) -} - -type Result interface { - GetRow() (Row, error) - MoreResults() bool - NextResult() (Result, error) - - Fields() []*Field - Map(string) int - Message() string - AffectedRows() uint64 - InsertId() uint64 - WarnCount() int - - GetRows() ([]Row, error) - End() error -} - -var New func(proto, laddr, raddr, user, passwd string, db ...string) Conn diff --git a/src/github.com/ziutek/mymysql/mysql/row.go b/src/github.com/ziutek/mymysql/mysql/row.go deleted file mode 100644 index 0338680..0000000 --- a/src/github.com/ziutek/mymysql/mysql/row.go +++ /dev/null @@ -1,432 +0,0 @@ -package mysql - -import ( - "bytes" - "errors" - "fmt" - "math" - "os" - "reflect" - "strconv" - "time" -) - -var errRange = errors.New("mysql: value out of range") - -// Result row - contains values for any column of received row. -// -// If row is a result of ordinary text query, its element can be -// []byte slice, contained result text or nil if NULL is returned. -// -// If it is result of prepared statement execution, its element field can be: -// intX, uintX, floatX, []byte, Date, Time, time.Time (in Local location) or nil -type Row []interface{} - -// Get the nn-th value and return it as []byte ([]byte{} if NULL) -func (tr Row) Bin(nn int) (bin []byte) { - switch data := tr[nn].(type) { - case nil: - // bin = []byte{} - case []byte: - bin = data - default: - buf := new(bytes.Buffer) - fmt.Fprint(buf, data) - bin = buf.Bytes() - } - return -} - -// Get the nn-th value and return it as string ("" if NULL) -func (tr Row) Str(nn int) (str string) { - switch data := tr[nn].(type) { - case nil: - // str = "" - case []byte: - str = string(data) - case time.Time: - str = TimeString(data) - case time.Duration: - str = DurationString(data) - default: - str = fmt.Sprint(data) - } - return -} - -const _MAX_INT = int(^uint(0) >> 1) -const _MIN_INT = -_MAX_INT - 1 - -// Get the nn-th value and return it as int (0 if NULL). Return error if -// conversion is impossible. -func (tr Row) IntErr(nn int) (val int, err error) { - fn := "IntErr" - switch data := tr[nn].(type) { - case nil: - // nop - case int32: - val = int(data) - case int16: - val = int(data) - case uint16: - val = int(data) - case int8: - val = int(data) - case uint8: - val = int(data) - case []byte: - val, err = strconv.Atoi(string(data)) - case int64: - if data >= int64(_MIN_INT) && data <= int64(_MAX_INT) { - val = int(data) - } else { - err = &strconv.NumError{fn, fmt.Sprint(data), errRange} - } - case uint32: - if data <= uint32(_MAX_INT) { - val = int(data) - } else { - err = &strconv.NumError{fn, fmt.Sprint(data), errRange} - } - case uint64: - if data <= uint64(_MAX_INT) { - val = int(data) - } else { - err = &strconv.NumError{fn, fmt.Sprint(data), errRange} - } - default: - err = &strconv.NumError{fn, fmt.Sprint(data), os.ErrInvalid} - } - return -} - -// Get the nn-th value and return it as int (0 if NULL). Panic if conversion is -// impossible. -func (tr Row) MustInt(nn int) (val int) { - val, err := tr.IntErr(nn) - if err != nil { - panic(err) - } - return -} - -// Get the nn-th value and return it as int. Return 0 if value is NULL or -// conversion is impossible. -func (tr Row) Int(nn int) (val int) { - val, _ = tr.IntErr(nn) - return -} - -const _MAX_UINT = ^uint(0) - -// Get the nn-th value and return it as uint (0 if NULL). Return error if -// conversion is impossible. -func (tr Row) UintErr(nn int) (val uint, err error) { - fn := "UintErr" - switch data := tr[nn].(type) { - case nil: - // nop - case uint32: - val = uint(data) - case uint16: - val = uint(data) - case uint8: - val = uint(data) - case []byte: - var v uint64 - v, err = strconv.ParseUint(string(data), 0, 0) - val = uint(v) - case uint64: - if data <= uint64(_MAX_UINT) { - val = uint(data) - } else { - err = &strconv.NumError{fn, fmt.Sprint(data), errRange} - } - case int8, int16, int32, int64: - v := reflect.ValueOf(data).Int() - if v >= 0 && v <= int64(_MAX_UINT) { - val = uint(v) - } else { - err = &strconv.NumError{fn, fmt.Sprint(data), errRange} - } - default: - err = &strconv.NumError{fn, fmt.Sprint(data), os.ErrInvalid} - } - return -} - -// Get the nn-th value and return it as uint (0 if NULL). Panic if conversion is -// impossible. -func (tr Row) MustUint(nn int) (val uint) { - val, err := tr.UintErr(nn) - if err != nil { - panic(err) - } - return -} - -// Get the nn-th value and return it as uint. Return 0 if value is NULL or -// conversion is impossible. -func (tr Row) Uint(nn int) (val uint) { - val, _ = tr.UintErr(nn) - return -} - -// Get the nn-th value and return it as Date (0000-00-00 if NULL). Return error -// if conversion is impossible. -func (tr Row) DateErr(nn int) (val Date, err error) { - switch data := tr[nn].(type) { - case nil: - // nop - case Date: - val = data - case []byte: - val, err = ParseDate(string(data)) - } - return -} - -// It is like DateErr but panics if conversion is impossible. -func (tr Row) MustDate(nn int) (val Date) { - val, err := tr.DateErr(nn) - if err != nil { - panic(err) - } - return -} - -// It is like DateErr but return 0000-00-00 if conversion is impossible. -func (tr Row) Date(nn int) (val Date) { - val, _ = tr.DateErr(nn) - return -} - -func convertTime(t time.Time, loc *time.Location) time.Time { - y, mon, d := t.Date() - h, m, s := t.Clock() - return time.Date(y, mon, d, h, m, s, t.Nanosecond(), loc) -} - -// Get the nn-th value and return it as time.Time in loc location (zero if NULL) -// Returns error if conversion is impossible. It can convert Date to time.Time. -func (tr Row) TimeErr(nn int, loc *time.Location) (t time.Time, err error) { - switch data := tr[nn].(type) { - case nil: - // nop - case time.Time: - t = data.In(loc) - if loc != time.Local { - t = convertTime(t, loc) - } - case Date: - t = data.Time(loc) - case []byte: - t, err = ParseTime(string(data), loc) - } - return -} - -// As TimeErr but panics if conversion is impossible. -func (tr Row) MustTime(nn int, loc *time.Location) (val time.Time) { - val, err := tr.TimeErr(nn, loc) - if err != nil { - panic(err) - } - return -} - -// It is like TimeErr but returns 0000-00-00 00:00:00 if conversion is -// impossible. -func (tr Row) Time(nn int, loc *time.Location) (val time.Time) { - val, _ = tr.TimeErr(nn, loc) - return -} - -// Get the nn-th value and return it as time.Time in Local location -// (zero if NULL). Returns error if conversion is impossible. -// It can convert Date to time.Time. -func (tr Row) LocaltimeErr(nn int) (t time.Time, err error) { - switch data := tr[nn].(type) { - case nil: - // nop - case time.Time: - t = data - case Date: - t = data.Time(time.Local) - case []byte: - t, err = ParseTime(string(data), time.Local) - } - return -} - -// As LocaltimeErr but panics if conversion is impossible. -func (tr Row) MustLocaltime(nn int) (val time.Time) { - val, err := tr.LocaltimeErr(nn) - if err != nil { - panic(err) - } - return -} - -// It is like LocaltimeErr but returns 0000-00-00 00:00:00 if conversion is -// impossible. -func (tr Row) Localtime(nn int) (val time.Time) { - val, _ = tr.LocaltimeErr(nn) - return -} - -// Get the nn-th value and return it as time.Duration (0 if NULL). Return error -// if conversion is impossible. -func (tr Row) DurationErr(nn int) (val time.Duration, err error) { - switch data := tr[nn].(type) { - case nil: - case time.Duration: - val = data - case []byte: - val, err = ParseDuration(string(data)) - default: - err = errors.New( - fmt.Sprintf("Can't convert `%v` to time.Duration", data), - ) - } - return -} - -// It is like DurationErr but panics if conversion is impossible. -func (tr Row) MustDuration(nn int) (val time.Duration) { - val, err := tr.DurationErr(nn) - if err != nil { - panic(err) - } - return -} - -// It is like DurationErr but return 0 if conversion is impossible. -func (tr Row) Duration(nn int) (val time.Duration) { - val, _ = tr.DurationErr(nn) - return -} - -// Get the nn-th value and return it as bool. Return error -// if conversion is impossible. -func (tr Row) BoolErr(nn int) (val bool, err error) { - fn := "BoolErr" - switch data := tr[nn].(type) { - case nil: - // nop - case int8: - val = (data != 0) - case int32: - val = (data != 0) - case int16: - val = (data != 0) - case int64: - val = (data != 0) - case uint8: - val = (data != 0) - case uint32: - val = (data != 0) - case uint16: - val = (data != 0) - case uint64: - val = (data != 0) - default: - err = &strconv.NumError{fn, fmt.Sprint(data), os.ErrInvalid} - } - return -} - -// It is like BoolErr but panics if conversion is impossible. -func (tr Row) MustBool(nn int) (val bool) { - val, err := tr.BoolErr(nn) - if err != nil { - panic(err) - } - return -} - -// It is like BoolErr but return false if conversion is impossible. -func (tr Row) Bool(nn int) (val bool) { - val, _ = tr.BoolErr(nn) - return -} - -// Get the nn-th value and return it as int64 (0 if NULL). Return error if -// conversion is impossible. -func (tr Row) Int64Err(nn int) (val int64, err error) { - fn := "Int64Err" - switch data := tr[nn].(type) { - case nil: - // nop - case int64, int32, int16, int8: - val = reflect.ValueOf(data).Int() - case uint64, uint32, uint16, uint8: - u := reflect.ValueOf(data).Uint() - if u > math.MaxInt64 { - err = &strconv.NumError{fn, fmt.Sprint(data), errRange} - } - val = int64(u) - case []byte: - val, err = strconv.ParseInt(string(data), 10, 64) - default: - err = &strconv.NumError{fn, fmt.Sprint(data), os.ErrInvalid} - } - return -} - -// Get the nn-th value and return it as int64 (0 if NULL). -// Panic if conversion is impossible. -func (tr Row) MustInt64(nn int) (val int64) { - val, err := tr.Int64Err(nn) - if err != nil { - panic(err) - } - return -} - -// Get the nn-th value and return it as int64. Return 0 if value is NULL or -// conversion is impossible. -func (tr Row) Int64(nn int) (val int64) { - val, _ = tr.Int64Err(nn) - return -} - -// Get the nn-th value and return it as uint64 (0 if NULL). Return error if -// conversion is impossible. -func (tr Row) Uint64Err(nn int) (val uint64, err error) { - fn := "Uint64Err" - switch data := tr[nn].(type) { - case nil: - // nop - case uint64, uint32, uint16, uint8: - val = reflect.ValueOf(data).Uint() - case int64, int32, int16, int8: - i := reflect.ValueOf(data).Int() - if i < 0 { - err = &strconv.NumError{fn, fmt.Sprint(data), errRange} - } - val = uint64(i) - case []byte: - val, err = strconv.ParseUint(string(data), 10, 64) - default: - err = &strconv.NumError{fn, fmt.Sprint(data), os.ErrInvalid} - } - return -} - -// Get the nn-th value and return it as uint64 (0 if NULL). -// Panic if conversion is impossible. -func (tr Row) MustUint64(nn int) (val uint64) { - val, err := tr.Uint64Err(nn) - if err != nil { - panic(err) - } - return -} - -// Get the nn-th value and return it as uint64. Return 0 if value is NULL or -// conversion is impossible. -func (tr Row) Uint64(nn int) (val uint64) { - val, _ = tr.Uint64Err(nn) - return -} diff --git a/src/github.com/ziutek/mymysql/mysql/types.go b/src/github.com/ziutek/mymysql/mysql/types.go deleted file mode 100644 index 9046839..0000000 --- a/src/github.com/ziutek/mymysql/mysql/types.go +++ /dev/null @@ -1,214 +0,0 @@ -package mysql - -import ( - "errors" - "fmt" - "strconv" - "strings" - "time" -) - -// For MySQL DATE type -type Date struct { - Year int16 - Month, Day byte -} - -func (dd Date) String() string { - return fmt.Sprintf("%04d-%02d-%02d", dd.Year, dd.Month, dd.Day) -} - -// True if date is 0000-00-00 -func (dd Date) IsZero() bool { - return dd.Day == 0 && dd.Month == 0 && dd.Year == 0 -} - -// Converts Date to time.Time using loc location. -// Converts MySQL zero to time.Time zero. -func (dd Date) Time(loc *time.Location) (t time.Time) { - if !dd.IsZero() { - t = time.Date( - int(dd.Year), time.Month(dd.Month), int(dd.Day), - 0, 0, 0, 0, - loc, - ) - } - return -} - -// Converts Date to time.Time using Local location. -// Converts MySQL zero to time.Time zero. -func (dd Date) Localtime() time.Time { - return dd.Time(time.Local) -} - -// Convert string date in format YYYY-MM-DD to Date. -// Leading and trailing spaces are ignored. If format is invalid returns zero. -func ParseDate(str string) (dd Date, err error) { - str = strings.TrimSpace(str) - if str == "0000-00-00" { - return - } - var ( - y, m, d int - ) - if len(str) != 10 || str[4] != '-' || str[7] != '-' { - goto invalid - } - if y, err = strconv.Atoi(str[0:4]); err != nil { - return - } - if m, err = strconv.Atoi(str[5:7]); err != nil { - return - } - if m < 1 || m > 12 { - goto invalid - } - if d, err = strconv.Atoi(str[8:10]); err != nil { - return - } - if d < 1 || d > 31 { - goto invalid - } - dd.Year = int16(y) - dd.Month = byte(m) - dd.Day = byte(d) - return - -invalid: - err = errors.New("Invalid MySQL DATE string: " + str) - return -} - -// Sandard MySQL datetime format -const TimeFormat = "2006-01-02 15:04:05.000000000" - -// Returns t as string in MySQL format Converts time.Time zero to MySQL zero. -func TimeString(t time.Time) string { - if t.IsZero() { - return "0000-00-00 00:00:00" - } - if t.Nanosecond() == 0 { - return t.Format(TimeFormat[:19]) - } - return t.Format(TimeFormat) -} - -// Parses string datetime in TimeFormat using loc location. -// Converts MySQL zero to time.Time zero. -func ParseTime(str string, loc *time.Location) (t time.Time, err error) { - str = strings.TrimSpace(str) - format := TimeFormat[:19] - switch len(str) { - case 10: - if str == "0000-00-00" { - return - } - format = format[:10] - case 19: - if str == "0000-00-00 00:00:00" { - return - } - } - // Don't expect 0000-00-00 00:00:00.0+ - t, err = time.Parse(format, str) - if err == nil && loc != time.UTC { - t = convertTime(t, loc) - } - return -} - -// Convert time.Duration to string representation of mysql.TIME -func DurationString(d time.Duration) string { - sign := 1 - if d < 0 { - sign = -1 - d = -d - } - ns := int(d % 1e9) - d /= 1e9 - sec := int(d % 60) - d /= 60 - min := int(d % 60) - hour := int(d/60) * sign - if ns == 0 { - return fmt.Sprintf("%d:%02d:%02d", hour, min, sec) - } - return fmt.Sprintf("%d:%02d:%02d.%09d", hour, min, sec, ns) -} - -// Parse duration from MySQL string format [+-]H+:MM:SS[.UUUUUUUUU]. -// Leading and trailing spaces are ignored. If format is invalid returns nil. -func ParseDuration(str string) (dur time.Duration, err error) { - str = strings.TrimSpace(str) - orig := str - // Check sign - sign := int64(1) - switch str[0] { - case '-': - sign = -1 - fallthrough - case '+': - str = str[1:] - } - var i, d int64 - // Find houre - if nn := strings.IndexRune(str, ':'); nn != -1 { - if i, err = strconv.ParseInt(str[0:nn], 10, 64); err != nil { - return - } - d = i * 3600 - str = str[nn+1:] - } else { - goto invalid - } - if len(str) != 5 && len(str) != 15 || str[2] != ':' { - goto invalid - } - if i, err = strconv.ParseInt(str[0:2], 10, 64); err != nil { - return - } - if i < 0 || i > 59 { - goto invalid - } - d += i * 60 - if i, err = strconv.ParseInt(str[3:5], 10, 64); err != nil { - return - } - if i < 0 || i > 59 { - goto invalid - } - d += i - d *= 1e9 - if len(str) == 15 { - if str[5] != '.' { - goto invalid - } - if i, err = strconv.ParseInt(str[6:15], 10, 64); err != nil { - return - } - d += i - } - dur = time.Duration(d * sign) - return - -invalid: - err = errors.New("invalid MySQL TIME string: " + orig) - return - -} - -type Blob []byte - -type Raw struct { - Typ uint16 - Val *[]byte -} - -type Timestamp struct { - time.Time -} - -func (t Timestamp) String() string { - return TimeString(t.Time) -} diff --git a/src/github.com/ziutek/mymysql/mysql/types_test.go b/src/github.com/ziutek/mymysql/mysql/types_test.go deleted file mode 100644 index 2eef258..0000000 --- a/src/github.com/ziutek/mymysql/mysql/types_test.go +++ /dev/null @@ -1,82 +0,0 @@ -package mysql - -import ( - "testing" - "time" -) - -type sio struct { - in, out string -} - -func checkRow(t *testing.T, examples []sio, conv func(string) interface{}) { - row := make(Row, 1) - for _, ex := range examples { - row[0] = conv(ex.in) - str := row.Str(0) - if str != ex.out { - t.Fatalf("Wrong conversion: '%s' != '%s'", str, ex.out) - } - } -} - -var dates = []sio{ - sio{"2121-11-22", "2121-11-22"}, - sio{"0000-00-00", "0000-00-00"}, - sio{" 1234-12-18 ", "1234-12-18"}, - sio{"\t1234-12-18 \r\n", "1234-12-18"}, -} - -func TestConvDate(t *testing.T) { - conv := func(str string) interface{} { - d, err := ParseDate(str) - if err != nil { - return err - } - return d - } - checkRow(t, dates, conv) -} - -var datetimes = []sio{ - sio{"2121-11-22 11:22:32", "2121-11-22 11:22:32"}, - sio{" 1234-12-18 22:11:22 ", "1234-12-18 22:11:22"}, - sio{"\t 1234-12-18 22:11:22 \r\n", "1234-12-18 22:11:22"}, - sio{"2000-11-11", "2000-11-11 00:00:00"}, - sio{"0000-00-00 00:00:00", "0000-00-00 00:00:00"}, - sio{"0000-00-00", "0000-00-00 00:00:00"}, - sio{"2000-11-22 11:11:11.000111222", "2000-11-22 11:11:11.000111222"}, -} - -func TestConvTime(t *testing.T) { - conv := func(str string) interface{} { - d, err := ParseTime(str, time.Local) - if err != nil { - return err - } - return d - } - checkRow(t, datetimes, conv) -} - -var times = []sio{ - sio{"1:23:45", "1:23:45"}, - sio{"-112:23:45", "-112:23:45"}, - sio{"+112:23:45", "112:23:45"}, - sio{"1:60:00", "invalid MySQL TIME string: 1:60:00"}, - sio{"1:00:60", "invalid MySQL TIME string: 1:00:60"}, - sio{"1:23:45.000111333", "1:23:45.000111333"}, - sio{"-1:23:45.000111333", "-1:23:45.000111333"}, -} - -func TestConvDuration(t *testing.T) { - conv := func(str string) interface{} { - d, err := ParseDuration(str) - if err != nil { - return err - } - return d - - } - checkRow(t, times, conv) -} diff --git a/src/github.com/ziutek/mymysql/mysql/utils.go b/src/github.com/ziutek/mymysql/mysql/utils.go deleted file mode 100644 index 4de4a45..0000000 --- a/src/github.com/ziutek/mymysql/mysql/utils.go +++ /dev/null @@ -1,52 +0,0 @@ -package mysql - -// This call Start and next call GetRow as long as it reads all rows from the -// result. Next it returns all readed rows as the slice of rows. -func Query(c Conn, sql string, params ...interface{}) (rows []Row, res Result, err error) { - res, err = c.Start(sql, params...) - if err != nil { - return - } - rows, err = GetRows(res) - return -} - -// This call Run and next call GetRow as long as it reads all rows from the -// result. Next it returns all readed rows as the slice of rows. -func Exec(s Stmt, params ...interface{}) (rows []Row, res Result, err error) { - res, err = s.Run(params...) - if err != nil { - return - } - rows, err = GetRows(res) - return -} - -// Read all unreaded rows and discard them. This function is useful if you -// don't want to use the remaining rows. It has an impact only on current -// result. If there is multi result query, you must use NextResult method and -// read/discard all rows in this result, before use other method that sends -// data to the server. You can't use this function if last GetRow returned nil. -func End(r Result) (err error) { - var row Row - for { - row, err = r.GetRow() - if err != nil || row == nil { - break - } - } - return -} - -// Reads all rows from result and returns them as slice. -func GetRows(r Result) (rows []Row, err error) { - var row Row - for { - row, err = r.GetRow() - if err != nil || row == nil { - break - } - rows = append(rows, row) - } - return -} diff --git a/src/github.com/ziutek/mymysql/native/LICENSE b/src/github.com/ziutek/mymysql/native/LICENSE deleted file mode 100644 index 8eab9a6..0000000 --- a/src/github.com/ziutek/mymysql/native/LICENSE +++ /dev/null @@ -1,24 +0,0 @@ -Copyright (c) 2010, Michal Derkacz -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: -1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. -2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. -3. The name of the author may not be used to endorse or promote products - derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR -IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT -NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/src/github.com/ziutek/mymysql/native/Makefile b/src/github.com/ziutek/mymysql/native/Makefile deleted file mode 100644 index ad1f37d..0000000 --- a/src/github.com/ziutek/mymysql/native/Makefile +++ /dev/null @@ -1,19 +0,0 @@ -include $(GOROOT)/src/Make.inc - -TARG=github.com/ziutek/mymysql/native -GOFILES=\ - mysql.go\ - init.go\ - common.go\ - packet.go\ - codecs.go\ - errors.go\ - consts.go\ - command.go\ - result.go\ - addons.go\ - prepared.go\ - binding.go\ - unsafe.go\ - -include $(GOROOT)/src/Make.pkg diff --git a/src/github.com/ziutek/mymysql/native/addons.go b/src/github.com/ziutek/mymysql/native/addons.go deleted file mode 100644 index 82ebe90..0000000 --- a/src/github.com/ziutek/mymysql/native/addons.go +++ /dev/null @@ -1,17 +0,0 @@ -package native - -func NbinToNstr(nbin *[]byte) *string { - if nbin == nil { - return nil - } - str := string(*nbin) - return &str -} - -func NstrToNbin(nstr *string) *[]byte { - if nstr == nil { - return nil - } - bin := []byte(*nstr) - return &bin -} diff --git a/src/github.com/ziutek/mymysql/native/bind_test.go b/src/github.com/ziutek/mymysql/native/bind_test.go deleted file mode 100644 index 550e613..0000000 --- a/src/github.com/ziutek/mymysql/native/bind_test.go +++ /dev/null @@ -1,316 +0,0 @@ -package native - -import ( - "bytes" - "github.com/ziutek/mymysql/mysql" - "math" - "reflect" - "testing" - "time" -) - -var ( - Bytes = []byte("Ala ma Kota!") - String = "ssss" //"A kot ma Alę!" - blob = mysql.Blob{1, 2, 3} - dateT = time.Date(2010, 12, 30, 17, 21, 01, 0, time.Local) - tstamp = mysql.Timestamp{dateT.Add(1e9)} - date = mysql.Date{Year: 2011, Month: 2, Day: 3} - tim = -time.Duration((5*24*3600+4*3600+3*60+2)*1e9 + 1) - bol = true - - pBytes *[]byte - pString *string - pBlob *mysql.Blob - pDateT *time.Time - pTstamp *mysql.Timestamp - pDate *mysql.Date - pTim *time.Duration - pBol *bool - - raw = mysql.Raw{MYSQL_TYPE_INT24, &[]byte{3, 2, 1}} - - Int8 = int8(1) - Uint8 = uint8(2) - Int16 = int16(3) - Uint16 = uint16(4) - Int32 = int32(5) - Uint32 = uint32(6) - Int64 = int64(0x7000100020003001) - Uint64 = uint64(0xffff0000ffff0000) - Int = int(7) - Uint = uint(8) - - Float32 = float32(1e10) - Float64 = 256e256 - - pInt8 *int8 - pUint8 *uint8 - pInt16 *int16 - pUint16 *uint16 - pInt32 *int32 - pUint32 *uint32 - pInt64 *int64 - pUint64 *uint64 - pInt *int - pUint *uint - pFloat32 *float32 - pFloat64 *float64 -) - -type BindTest struct { - val interface{} - typ uint16 - length int -} - -var bindTests = []BindTest{ - BindTest{nil, MYSQL_TYPE_NULL, 0}, - - BindTest{Bytes, MYSQL_TYPE_VAR_STRING, -1}, - BindTest{String, MYSQL_TYPE_STRING, -1}, - BindTest{blob, MYSQL_TYPE_BLOB, -1}, - BindTest{dateT, MYSQL_TYPE_DATETIME, -1}, - BindTest{tstamp, MYSQL_TYPE_TIMESTAMP, -1}, - BindTest{date, MYSQL_TYPE_DATE, -1}, - BindTest{tim, MYSQL_TYPE_TIME, -1}, - BindTest{bol, MYSQL_TYPE_TINY, -1}, - - BindTest{&Bytes, MYSQL_TYPE_VAR_STRING, -1}, - BindTest{&String, MYSQL_TYPE_STRING, -1}, - BindTest{&blob, MYSQL_TYPE_BLOB, -1}, - BindTest{&dateT, MYSQL_TYPE_DATETIME, -1}, - BindTest{&tstamp, MYSQL_TYPE_TIMESTAMP, -1}, - BindTest{&date, MYSQL_TYPE_DATE, -1}, - BindTest{&tim, MYSQL_TYPE_TIME, -1}, - - BindTest{pBytes, MYSQL_TYPE_VAR_STRING, -1}, - BindTest{pString, MYSQL_TYPE_STRING, -1}, - BindTest{pBlob, MYSQL_TYPE_BLOB, -1}, - BindTest{pDateT, MYSQL_TYPE_DATETIME, -1}, - BindTest{pTstamp, MYSQL_TYPE_TIMESTAMP, -1}, - BindTest{pDate, MYSQL_TYPE_DATE, -1}, - BindTest{pTim, MYSQL_TYPE_TIME, -1}, - BindTest{pBol, MYSQL_TYPE_TINY, -1}, - - BindTest{raw, MYSQL_TYPE_INT24, -1}, - - BindTest{Int8, MYSQL_TYPE_TINY, 1}, - BindTest{Int16, MYSQL_TYPE_SHORT, 2}, - BindTest{Int32, MYSQL_TYPE_LONG, 4}, - BindTest{Int64, MYSQL_TYPE_LONGLONG, 8}, - BindTest{Int, MYSQL_TYPE_LONG, 4}, // Hack - - BindTest{&Int8, MYSQL_TYPE_TINY, 1}, - BindTest{&Int16, MYSQL_TYPE_SHORT, 2}, - BindTest{&Int32, MYSQL_TYPE_LONG, 4}, - BindTest{&Int64, MYSQL_TYPE_LONGLONG, 8}, - BindTest{&Int, MYSQL_TYPE_LONG, 4}, // Hack - - BindTest{pInt8, MYSQL_TYPE_TINY, 1}, - BindTest{pInt16, MYSQL_TYPE_SHORT, 2}, - BindTest{pInt32, MYSQL_TYPE_LONG, 4}, - BindTest{pInt64, MYSQL_TYPE_LONGLONG, 8}, - BindTest{pInt, MYSQL_TYPE_LONG, 4}, // Hack - - BindTest{Uint8, MYSQL_TYPE_TINY | MYSQL_UNSIGNED_MASK, 1}, - BindTest{Uint16, MYSQL_TYPE_SHORT | MYSQL_UNSIGNED_MASK, 2}, - BindTest{Uint32, MYSQL_TYPE_LONG | MYSQL_UNSIGNED_MASK, 4}, - BindTest{Uint64, MYSQL_TYPE_LONGLONG | MYSQL_UNSIGNED_MASK, 8}, - BindTest{Uint, MYSQL_TYPE_LONG | MYSQL_UNSIGNED_MASK, 4}, //Hack - - BindTest{&Uint8, MYSQL_TYPE_TINY | MYSQL_UNSIGNED_MASK, 1}, - BindTest{&Uint16, MYSQL_TYPE_SHORT | MYSQL_UNSIGNED_MASK, 2}, - BindTest{&Uint32, MYSQL_TYPE_LONG | MYSQL_UNSIGNED_MASK, 4}, - BindTest{&Uint64, MYSQL_TYPE_LONGLONG | MYSQL_UNSIGNED_MASK, 8}, - BindTest{&Uint, MYSQL_TYPE_LONG | MYSQL_UNSIGNED_MASK, 4}, //Hack - - BindTest{pUint8, MYSQL_TYPE_TINY | MYSQL_UNSIGNED_MASK, 1}, - BindTest{pUint16, MYSQL_TYPE_SHORT | MYSQL_UNSIGNED_MASK, 2}, - BindTest{pUint32, MYSQL_TYPE_LONG | MYSQL_UNSIGNED_MASK, 4}, - BindTest{pUint64, MYSQL_TYPE_LONGLONG | MYSQL_UNSIGNED_MASK, 8}, - BindTest{pUint, MYSQL_TYPE_LONG | MYSQL_UNSIGNED_MASK, 4}, //Hack - - BindTest{Float32, MYSQL_TYPE_FLOAT, 4}, - BindTest{Float64, MYSQL_TYPE_DOUBLE, 8}, - - BindTest{&Float32, MYSQL_TYPE_FLOAT, 4}, - BindTest{&Float64, MYSQL_TYPE_DOUBLE, 8}, -} - -func makeAddressable(v reflect.Value) reflect.Value { - if v.IsValid() { - // Make an addresable value - av := reflect.New(v.Type()).Elem() - av.Set(v) - v = av - } - return v -} - -func TestBind(t *testing.T) { - for _, test := range bindTests { - v := makeAddressable(reflect.ValueOf(test.val)) - val := bindValue(v) - if val.typ != test.typ || val.length != test.length { - t.Errorf( - "Type: %s exp=0x%x res=0x%x Len: exp=%d res=%d", - reflect.TypeOf(test.val), test.typ, val.typ, test.length, - val.length, - ) - } - } -} - -type WriteTest struct { - val interface{} - exp []byte -} - -var writeTest []WriteTest - -func init() { - b := make([]byte, 64*1024) - for ii := range b { - b[ii] = byte(ii) - } - blob = mysql.Blob(b) - - writeTest = []WriteTest{ - WriteTest{Bytes, append([]byte{byte(len(Bytes))}, Bytes...)}, - WriteTest{String, append([]byte{byte(len(String))}, []byte(String)...)}, - WriteTest{pBytes, nil}, - WriteTest{pString, nil}, - WriteTest{ - blob, - append(append([]byte{253}, EncodeU24(uint32(len(blob)))...), - []byte(blob)...), - }, - WriteTest{ - dateT, - []byte{ - 7, byte(dateT.Year()), byte(dateT.Year() >> 8), - byte(dateT.Month()), - byte(dateT.Day()), byte(dateT.Hour()), byte(dateT.Minute()), - byte(dateT.Second()), - }, - }, - WriteTest{ - &dateT, - []byte{ - 7, byte(dateT.Year()), byte(dateT.Year() >> 8), - byte(dateT.Month()), - byte(dateT.Day()), byte(dateT.Hour()), byte(dateT.Minute()), - byte(dateT.Second()), - }, - }, - WriteTest{ - date, - []byte{ - 4, byte(date.Year), byte(date.Year >> 8), byte(date.Month), - byte(date.Day), - }, - }, - WriteTest{ - &date, - []byte{ - 4, byte(date.Year), byte(date.Year >> 8), byte(date.Month), - byte(date.Day), - }, - }, - WriteTest{ - tim, - []byte{12, 1, 5, 0, 0, 0, 4, 3, 2, 1, 0, 0, 0}, - }, - WriteTest{ - &tim, - []byte{12, 1, 5, 0, 0, 0, 4, 3, 2, 1, 0, 0, 0}, - }, - WriteTest{bol, []byte{1}}, - WriteTest{&bol, []byte{1}}, - WriteTest{pBol, nil}, - - WriteTest{dateT, EncodeTime(dateT)}, - WriteTest{&dateT, EncodeTime(dateT)}, - WriteTest{pDateT, nil}, - - WriteTest{tstamp, EncodeTime(tstamp.Time)}, - WriteTest{&tstamp, EncodeTime(tstamp.Time)}, - WriteTest{pTstamp, nil}, - - WriteTest{date, EncodeDate(date)}, - WriteTest{&date, EncodeDate(date)}, - WriteTest{pDate, nil}, - - WriteTest{tim, EncodeDuration(tim)}, - WriteTest{&tim, EncodeDuration(tim)}, - WriteTest{pTim, nil}, - - WriteTest{Int, EncodeU32(uint32(Int))}, // Hack - WriteTest{Int16, EncodeU16(uint16(Int16))}, - WriteTest{Int32, EncodeU32(uint32(Int32))}, - WriteTest{Int64, EncodeU64(uint64(Int64))}, - - WriteTest{Int, EncodeU32(uint32(Int))}, // Hack - WriteTest{Uint16, EncodeU16(Uint16)}, - WriteTest{Uint32, EncodeU32(Uint32)}, - WriteTest{Uint64, EncodeU64(Uint64)}, - - WriteTest{&Int, EncodeU32(uint32(Int))}, // Hack - WriteTest{&Int16, EncodeU16(uint16(Int16))}, - WriteTest{&Int32, EncodeU32(uint32(Int32))}, - WriteTest{&Int64, EncodeU64(uint64(Int64))}, - - WriteTest{&Uint, EncodeU32(uint32(Uint))}, // Hack - WriteTest{&Uint16, EncodeU16(Uint16)}, - WriteTest{&Uint32, EncodeU32(Uint32)}, - WriteTest{&Uint64, EncodeU64(Uint64)}, - - WriteTest{pInt, nil}, - WriteTest{pInt16, nil}, - WriteTest{pInt32, nil}, - WriteTest{pInt64, nil}, - - WriteTest{Float32, EncodeU32(math.Float32bits(Float32))}, - WriteTest{Float64, EncodeU64(math.Float64bits(Float64))}, - - WriteTest{&Float32, EncodeU32(math.Float32bits(Float32))}, - WriteTest{&Float64, EncodeU64(math.Float64bits(Float64))}, - - WriteTest{pFloat32, nil}, - WriteTest{pFloat64, nil}, - } -} - -func TestWrite(t *testing.T) { - buf := new(bytes.Buffer) - for _, test := range writeTest { - buf.Reset() - v := makeAddressable(reflect.ValueOf(test.val)) - val := bindValue(v) - writeValue(buf, val) - if !bytes.Equal(buf.Bytes(), test.exp) || val.Len() != len(test.exp) { - t.Errorf("%s - exp_len=%d res_len=%d exp: %v res: %v", - reflect.TypeOf(test.val), len(test.exp), val.Len(), - test.exp, buf.Bytes(), - ) - } - } -} - -func TestEscapeString(t *testing.T) { - txt := " \000 \n \r \\ ' \" \032 " - exp := ` \0 \n \r \\ \' \" \Z ` - out := escapeString(txt) - if out != exp { - t.Fatalf("escapeString: ret='%s' exp='%s'", out, exp) - } -} -func TestEscapeQuotes(t *testing.T) { - txt := " '' '' ' ' ' " - exp := ` '''' '''' '' '' '' ` - out := escapeQuotes(txt) - if out != exp { - t.Fatalf("escapeString: ret='%s' exp='%s'", out, exp) - } -} diff --git a/src/github.com/ziutek/mymysql/native/binding.go b/src/github.com/ziutek/mymysql/native/binding.go deleted file mode 100644 index c706432..0000000 --- a/src/github.com/ziutek/mymysql/native/binding.go +++ /dev/null @@ -1,151 +0,0 @@ -package native - -import ( - "github.com/ziutek/mymysql/mysql" - "reflect" - "time" -) - -var ( - timeType = reflect.TypeOf(time.Time{}) - timestampType = reflect.TypeOf(mysql.Timestamp{}) - dateType = reflect.TypeOf(mysql.Date{}) - durationType = reflect.TypeOf(time.Duration(0)) - blobType = reflect.TypeOf(mysql.Blob{}) - rawType = reflect.TypeOf(mysql.Raw{}) -) - -// val should be an addressable value -func bindValue(val reflect.Value) (out *paramValue) { - if !val.IsValid() { - return ¶mValue{typ: MYSQL_TYPE_NULL} - } - typ := val.Type() - out = new(paramValue) - if typ.Kind() == reflect.Ptr { - // We have addressable pointer - out.SetAddr(val.UnsafeAddr()) - // Dereference pointer for next operation on its value - typ = typ.Elem() - val = val.Elem() - } else { - // We have addressable value. Create a pointer to it - pv := val.Addr() - // This pointer is unaddressable so copy it and return an address - ppv := reflect.New(pv.Type()) - ppv.Elem().Set(pv) - out.SetAddr(ppv.Pointer()) - } - - // Obtain value type - switch typ.Kind() { - case reflect.String: - out.typ = MYSQL_TYPE_STRING - out.length = -1 - return - - case reflect.Int: - out.typ = _INT_TYPE - out.length = _SIZE_OF_INT - return - - case reflect.Int8: - out.typ = MYSQL_TYPE_TINY - out.length = 1 - return - - case reflect.Int16: - out.typ = MYSQL_TYPE_SHORT - out.length = 2 - return - - case reflect.Int32: - out.typ = MYSQL_TYPE_LONG - out.length = 4 - return - - case reflect.Int64: - if typ == durationType { - out.typ = MYSQL_TYPE_TIME - out.length = -1 - return - } - out.typ = MYSQL_TYPE_LONGLONG - out.length = 8 - return - - case reflect.Uint: - out.typ = _INT_TYPE | MYSQL_UNSIGNED_MASK - out.length = _SIZE_OF_INT - return - - case reflect.Uint8: - out.typ = MYSQL_TYPE_TINY | MYSQL_UNSIGNED_MASK - out.length = 1 - return - - case reflect.Uint16: - out.typ = MYSQL_TYPE_SHORT | MYSQL_UNSIGNED_MASK - out.length = 2 - return - - case reflect.Uint32: - out.typ = MYSQL_TYPE_LONG | MYSQL_UNSIGNED_MASK - out.length = 4 - return - - case reflect.Uint64: - out.typ = MYSQL_TYPE_LONGLONG | MYSQL_UNSIGNED_MASK - out.length = 8 - return - - case reflect.Float32: - out.typ = MYSQL_TYPE_FLOAT - out.length = 4 - return - - case reflect.Float64: - out.typ = MYSQL_TYPE_DOUBLE - out.length = 8 - return - - case reflect.Slice: - out.length = -1 - if typ == blobType { - out.typ = MYSQL_TYPE_BLOB - return - } - if typ.Elem().Kind() == reflect.Uint8 { - out.typ = MYSQL_TYPE_VAR_STRING - return - } - - case reflect.Struct: - out.length = -1 - if typ == timeType { - out.typ = MYSQL_TYPE_DATETIME - return - } - if typ == dateType { - out.typ = MYSQL_TYPE_DATE - return - } - if typ == timestampType { - out.typ = MYSQL_TYPE_TIMESTAMP - return - } - if typ == rawType { - out.typ = val.FieldByName("Typ").Interface().(uint16) - out.SetAddr(val.FieldByName("Val").Pointer()) - out.raw = true - return - } - - case reflect.Bool: - out.typ = MYSQL_TYPE_TINY - // bool implementation isn't documented so we treat it in special way - out.length = -1 - return - } - panic(BIND_UNK_TYPE) -} diff --git a/src/github.com/ziutek/mymysql/native/codecs.go b/src/github.com/ziutek/mymysql/native/codecs.go deleted file mode 100644 index 4ddcdc1..0000000 --- a/src/github.com/ziutek/mymysql/native/codecs.go +++ /dev/null @@ -1,523 +0,0 @@ -package native - -import ( - "bytes" - "crypto/sha1" - "github.com/ziutek/mymysql/mysql" - "io" - "time" -) - -// Integers - -func DecodeU16(buf []byte) uint16 { - return uint16(buf[1])<<8 | uint16(buf[0]) -} -func readU16(rd io.Reader) uint16 { - buf := make([]byte, 2) - readFull(rd, buf) - return DecodeU16(buf) -} - -func DecodeU24(buf []byte) uint32 { - return (uint32(buf[2])<<8|uint32(buf[1]))<<8 | uint32(buf[0]) -} -func readU24(rd io.Reader) uint32 { - buf := make([]byte, 3) - readFull(rd, buf) - return DecodeU24(buf) -} - -func DecodeU32(buf []byte) uint32 { - return ((uint32(buf[3])<<8|uint32(buf[2]))<<8| - uint32(buf[1]))<<8 | uint32(buf[0]) -} -func readU32(rd io.Reader) uint32 { - buf := make([]byte, 4) - readFull(rd, buf) - return DecodeU32(buf) -} - -func DecodeU64(buf []byte) (rv uint64) { - for ii, vv := range buf { - rv |= uint64(vv) << uint(ii*8) - } - return -} -func readU64(rd io.Reader) (rv uint64) { - buf := make([]byte, 8) - readFull(rd, buf) - return DecodeU64(buf) -} - -func EncodeU16(val uint16) []byte { - return []byte{byte(val), byte(val >> 8)} -} -func writeU16(wr io.Writer, val uint16) { - write(wr, EncodeU16(val)) -} - -func EncodeU24(val uint32) []byte { - return []byte{byte(val), byte(val >> 8), byte(val >> 16)} -} -func writeU24(wr io.Writer, val uint32) { - write(wr, EncodeU24(val)) -} - -func EncodeU32(val uint32) []byte { - return []byte{byte(val), byte(val >> 8), byte(val >> 16), byte(val >> 24)} -} -func writeU32(wr io.Writer, val uint32) { - write(wr, EncodeU32(val)) -} - -func EncodeU64(val uint64) []byte { - buf := make([]byte, 8) - for ii := range buf { - buf[ii] = byte(val >> uint(ii*8)) - } - return buf -} -func writeU64(wr io.Writer, val uint64) { - write(wr, EncodeU64(val)) -} - -// Variable length values - -func readNullLCB(rd io.Reader) (lcb uint64, null bool) { - bb := readByte(rd) - switch bb { - case 251: - null = true - case 252: - lcb = uint64(readU16(rd)) - case 253: - lcb = uint64(readU24(rd)) - case 254: - lcb = readU64(rd) - default: - lcb = uint64(bb) - } - return -} - -func readLCB(rd io.Reader) uint64 { - lcb, null := readNullLCB(rd) - if null { - panic(UNEXP_NULL_LCB_ERROR) - } - return lcb -} - -func writeLCB(wr io.Writer, val uint64) { - switch { - case val <= 250: - writeByte(wr, byte(val)) - - case val <= 0xffff: - writeByte(wr, 252) - writeU16(wr, uint16(val)) - - case val <= 0xffffff: - writeByte(wr, 253) - writeU24(wr, uint32(val)) - - default: - writeByte(wr, 254) - writeU64(wr, val) - } -} - -func lenLCB(val uint64) int { - switch { - case val <= 250: - return 1 - - case val <= 0xffff: - return 3 - - case val <= 0xffffff: - return 4 - } - return 9 -} - -func readNullBin(rd io.Reader) (buf []byte, null bool) { - var l uint64 - l, null = readNullLCB(rd) - if null { - return - } - buf = make([]byte, l) - readFull(rd, buf) - return -} - -func readBin(rd io.Reader) []byte { - buf, null := readNullBin(rd) - if null { - panic(UNEXP_NULL_LCS_ERROR) - } - return buf -} - -func writeBin(wr io.Writer, buf []byte) { - writeLCB(wr, uint64(len(buf))) - write(wr, buf) -} - -func lenBin(buf []byte) int { - return lenLCB(uint64(len(buf))) + len(buf) -} - -func readStr(rd io.Reader) (str string) { - buf := readBin(rd) - str = string(buf) - return -} - -func writeStr(wr io.Writer, str string) { - writeLCB(wr, uint64(len(str))) - writeString(wr, str) -} - -func lenStr(str string) int { - return lenLCB(uint64(len(str))) + len(str) -} - -func writeLC(wr io.Writer, v interface{}) { - switch val := v.(type) { - case []byte: - writeBin(wr, val) - case *[]byte: - writeBin(wr, *val) - case string: - writeStr(wr, val) - case *string: - writeStr(wr, *val) - default: - panic("Unknown data type for write as lenght coded string") - } -} - -func lenLC(v interface{}) int { - switch val := v.(type) { - case []byte: - return lenBin(val) - case *[]byte: - return lenBin(*val) - case string: - return lenStr(val) - case *string: - return lenStr(*val) - } - panic("Unknown data type for write as lenght coded string") -} - -func readNTB(rd io.Reader) (buf []byte) { - bb := new(bytes.Buffer) - for { - ch := readByte(rd) - if ch == 0 { - return bb.Bytes() - } - bb.WriteByte(ch) - } - return -} - -func writeNTB(wr io.Writer, buf []byte) { - write(wr, buf) - writeByte(wr, 0) -} - -func readNTS(rd io.Reader) (str string) { - buf := readNTB(rd) - str = string(buf) - return -} - -func writeNTS(wr io.Writer, str string) { - writeNTB(wr, []byte(str)) -} - -func writeNT(wr io.Writer, v interface{}) { - switch val := v.(type) { - case []byte: - writeNTB(wr, val) - case string: - writeNTS(wr, val) - default: - panic("Unknown type for write as null terminated data") - } -} - -// Date and time - -func readDuration(rd io.Reader) time.Duration { - dlen := readByte(rd) - switch dlen { - case 251: - // Null - panic(UNEXP_NULL_TIME_ERROR) - case 0: - // 00:00:00 - return 0 - case 5, 8, 12: - // Properly time length - default: - panic(WRONG_DATE_LEN_ERROR) - } - buf := make([]byte, dlen) - readFull(rd, buf) - tt := int64(0) - switch dlen { - case 12: - // Nanosecond part - tt += int64(DecodeU32(buf[8:])) - fallthrough - case 8: - // HH:MM:SS part - tt += int64(int(buf[5])*3600+int(buf[6])*60+int(buf[7])) * 1e9 - fallthrough - case 5: - // Day part - tt += int64(DecodeU32(buf[1:5])) * (24 * 3600 * 1e9) - fallthrough - } - if buf[0] != 0 { - tt = -tt - } - return time.Duration(tt) -} - -func EncodeDuration(d time.Duration) []byte { - buf := make([]byte, 13) - if d < 0 { - buf[1] = 1 - d = -d - } - if ns := uint32(d % 1e9); ns != 0 { - copy(buf[9:13], EncodeU32(ns)) // nanosecond - buf[0] += 4 - } - d /= 1e9 - if hms := int(d % (24 * 3600)); buf[0] != 0 || hms != 0 { - buf[8] = byte(hms % 60) // second - hms /= 60 - buf[7] = byte(hms % 60) // minute - buf[6] = byte(hms / 60) // hour - buf[0] += 3 - } - if day := uint32(d / (24 * 3600)); buf[0] != 0 || day != 0 { - copy(buf[2:6], EncodeU32(day)) // day - buf[0] += 4 - } - buf[0]++ // For sign byte - buf = buf[0 : buf[0]+1] - return buf -} - -func writeDuration(wr io.Writer, d time.Duration) { - write(wr, EncodeDuration(d)) -} - -func lenDuration(d time.Duration) int { - if d == 0 { - return 2 - } - if d%1e9 != 0 { - return 13 - } - d /= 1e9 - if d%(24*3600) != 0 { - return 9 - } - return 6 -} - -func readTime(rd io.Reader) time.Time { - dlen := readByte(rd) - switch dlen { - case 251: - // Null - panic(UNEXP_NULL_DATE_ERROR) - case 0: - // return 0000-00-00 converted to time.Time zero - return time.Time{} - case 4, 7, 11: - // Properly datetime length - default: - panic(WRONG_DATE_LEN_ERROR) - } - - buf := make([]byte, dlen) - readFull(rd, buf) - var y, mon, d, h, m, s, n int - switch dlen { - case 11: - // 2006-01-02 15:04:05.001004005 - n = int(DecodeU32(buf[7:])) - fallthrough - case 7: - // 2006-01-02 15:04:05 - h = int(buf[4]) - m = int(buf[5]) - s = int(buf[6]) - fallthrough - case 4: - // 2006-01-02 - y = int(DecodeU16(buf[0:2])) - mon = int(buf[2]) - d = int(buf[3]) - } - return time.Date(y, time.Month(mon), d, h, m, s, n, time.Local) -} - -func encodeNonzeroTime(y int16, mon, d, h, m, s byte, n uint32) []byte { - buf := make([]byte, 12) - switch { - case n != 0: - copy(buf[7:12], EncodeU32(n)) - buf[0] += 4 - fallthrough - case s != 0 || m != 0 || h != 0: - buf[7] = s - buf[6] = m - buf[5] = h - buf[0] += 3 - } - buf[4] = d - buf[3] = mon - copy(buf[1:3], EncodeU16(uint16(y))) - buf[0] += 4 - buf = buf[0 : buf[0]+1] - return buf -} - -func EncodeTime(t time.Time) []byte { - if t.IsZero() { - return []byte{0} // MySQL zero - } - y, mon, d := t.Date() - h, m, s := t.Clock() - n := t.Nanosecond() - return encodeNonzeroTime( - int16(y), byte(mon), byte(d), - byte(h), byte(m), byte(s), uint32(n), - ) -} - -func writeTime(wr io.Writer, t time.Time) { - write(wr, EncodeTime(t)) -} - -func lenTime(t time.Time) int { - switch { - case t.IsZero(): - return 1 - case t.Nanosecond() != 0: - return 12 - case t.Second() != 0 || t.Minute() != 0 || t.Hour() != 0: - return 8 - } - return 5 -} - -func readDate(rd io.Reader) mysql.Date { - y, m, d := readTime(rd).Date() - return mysql.Date{int16(y), byte(m), byte(d)} -} - -func EncodeDate(d mysql.Date) []byte { - if d.IsZero() { - return []byte{0} // MySQL zero - } - return encodeNonzeroTime(d.Year, d.Month, d.Day, 0, 0, 0, 0) -} - -func writeDate(wr io.Writer, d mysql.Date) { - write(wr, EncodeDate(d)) -} - -func lenDate(d mysql.Date) int { - if d.IsZero() { - return 1 - } - return 5 -} - -// Borrowed from GoMySQL -// SHA1(SHA1(SHA1(password)), scramble) XOR SHA1(password) -func (my *Conn) encryptedPasswd() (out []byte) { - // Convert password to byte array - passbytes := []byte(my.passwd) - // stage1_hash = SHA1(password) - // SHA1 encode - crypt := sha1.New() - crypt.Write(passbytes) - stg1Hash := crypt.Sum(nil) - // token = SHA1(SHA1(stage1_hash), scramble) XOR stage1_hash - // SHA1 encode again - crypt.Reset() - crypt.Write(stg1Hash) - stg2Hash := crypt.Sum(nil) - // SHA1 2nd hash and scramble - crypt.Reset() - crypt.Write(my.info.scramble) - crypt.Write(stg2Hash) - stg3Hash := crypt.Sum(nil) - // XOR with first hash - out = make([]byte, len(my.info.scramble)) - for ii := range my.info.scramble { - out[ii] = stg3Hash[ii] ^ stg1Hash[ii] - } - return -} - -func escapeString(txt string) string { - var ( - esc string - buf bytes.Buffer - ) - last := 0 - for ii, bb := range txt { - switch bb { - case 0: - esc = `\0` - case '\n': - esc = `\n` - case '\r': - esc = `\r` - case '\\': - esc = `\\` - case '\'': - esc = `\'` - case '"': - esc = `\"` - case '\032': - esc = `\Z` - default: - continue - } - io.WriteString(&buf, txt[last:ii]) - io.WriteString(&buf, esc) - last = ii + 1 - } - io.WriteString(&buf, txt[last:]) - return buf.String() -} - -func escapeQuotes(txt string) string { - var buf bytes.Buffer - last := 0 - for ii, bb := range txt { - if bb == '\'' { - io.WriteString(&buf, txt[last:ii]) - io.WriteString(&buf, `''`) - last = ii + 1 - } - } - io.WriteString(&buf, txt[last:]) - return buf.String() -} diff --git a/src/github.com/ziutek/mymysql/native/command.go b/src/github.com/ziutek/mymysql/native/command.go deleted file mode 100644 index 980f4a9..0000000 --- a/src/github.com/ziutek/mymysql/native/command.go +++ /dev/null @@ -1,101 +0,0 @@ -package native - -import "log" - -func (my *Conn) sendCmd(cmd byte, argv ...interface{}) { - // Reset sequence number - my.seq = 0 - // Write command - switch cmd { - case _COM_QUERY, _COM_INIT_DB, _COM_CREATE_DB, _COM_DROP_DB, - _COM_STMT_PREPARE: - pw := my.newPktWriter(1 + lenBS(argv[0])) - writeByte(pw, cmd) - writeBS(pw, argv[0]) - - case _COM_STMT_SEND_LONG_DATA: - pw := my.newPktWriter(1 + 4 + 2 + lenBS(argv[2])) - writeByte(pw, cmd) - writeU32(pw, argv[0].(uint32)) // Statement ID - writeU16(pw, argv[1].(uint16)) // Parameter number - writeBS(pw, argv[2]) // payload - - case _COM_QUIT, _COM_STATISTICS, _COM_PROCESS_INFO, _COM_DEBUG, _COM_PING: - pw := my.newPktWriter(1) - writeByte(pw, cmd) - - case _COM_FIELD_LIST: - pay_len := 1 + lenBS(argv[0]) + 1 - if len(argv) > 1 { - pay_len += lenBS(argv[1]) - } - - pw := my.newPktWriter(pay_len) - writeByte(pw, cmd) - writeNT(pw, argv[0]) - if len(argv) > 1 { - writeBS(pw, argv[1]) - } - - case _COM_TABLE_DUMP: - pw := my.newPktWriter(1 + lenLC(argv[0]) + lenLC(argv[1])) - writeByte(pw, cmd) - writeLC(pw, argv[0]) - writeLC(pw, argv[1]) - - case _COM_REFRESH, _COM_SHUTDOWN: - pw := my.newPktWriter(1 + 1) - writeByte(pw, cmd) - writeByte(pw, argv[0].(byte)) - - case _COM_STMT_FETCH: - pw := my.newPktWriter(1 + 4 + 4) - writeByte(pw, cmd) - writeU32(pw, argv[0].(uint32)) - writeU32(pw, argv[1].(uint32)) - - case _COM_PROCESS_KILL, _COM_STMT_CLOSE, _COM_STMT_RESET: - pw := my.newPktWriter(1 + 4) - writeByte(pw, cmd) - writeU32(pw, argv[0].(uint32)) - - case _COM_SET_OPTION: - pw := my.newPktWriter(1 + 2) - writeByte(pw, cmd) - writeU16(pw, argv[0].(uint16)) - - case _COM_CHANGE_USER: - pw := my.newPktWriter( - 1 + lenBS(argv[0]) + 1 + lenLC(argv[1]) + lenBS(argv[2]) + 1, - ) - writeByte(pw, cmd) - writeNT(pw, argv[0]) // User name - writeLC(pw, argv[1]) // Scrambled password - writeNT(pw, argv[2]) // Database name - //writeU16(pw, argv[3]) // Character set number (since 5.1.23?) - - case _COM_BINLOG_DUMP: - pay_len := 1 + 4 + 2 + 4 - if len(argv) > 3 { - pay_len += lenBS(argv[3]) - } - - pw := my.newPktWriter(pay_len) - writeByte(pw, cmd) - writeU32(pw, argv[0].(uint32)) // Start position - writeU16(pw, argv[1].(uint16)) // Flags - writeU32(pw, argv[2].(uint32)) // Slave server id - if len(argv) > 3 { - writeBS(pw, argv[3]) - } - - // TODO: case COM_REGISTER_SLAVE: - - default: - panic("Unknown code for MySQL command") - } - - if my.Debug { - log.Printf("[%2d <-] Command packet: Cmd=0x%x", my.seq-1, cmd) - } -} diff --git a/src/github.com/ziutek/mymysql/native/common.go b/src/github.com/ziutek/mymysql/native/common.go deleted file mode 100644 index e58ca13..0000000 --- a/src/github.com/ziutek/mymysql/native/common.go +++ /dev/null @@ -1,86 +0,0 @@ -package native - -import ( - "io" - "runtime" -) - -var tab8s = " " - -func readFull(rd io.Reader, buf []byte) { - for nn := 0; nn < len(buf); { - kk, err := rd.Read(buf[nn:]) - nn += kk - if err != nil { - if err == io.EOF { - err = io.ErrUnexpectedEOF - } - panic(err) - } - } -} - -func read(rd io.Reader, nn int) (buf []byte) { - buf = make([]byte, nn) - readFull(rd, buf) - return -} - -func readByte(rd io.Reader) byte { - buf := make([]byte, 1) - if _, err := rd.Read(buf); err != nil { - if err == io.EOF { - err = io.ErrUnexpectedEOF - } - panic(err) - } - return buf[0] -} - -func write(wr io.Writer, buf []byte) { - if _, err := wr.Write(buf); err != nil { - panic(err) - } -} - -func writeByte(wr io.Writer, ch byte) { - write(wr, []byte{ch}) -} - -func writeString(wr io.Writer, str string) { - write(wr, []byte(str)) -} - -func writeBS(wr io.Writer, bs interface{}) { - switch buf := bs.(type) { - case string: - writeString(wr, buf) - case []byte: - write(wr, buf) - default: - panic("Can't write: argument isn't a string nor []byte") - } -} - -func lenBS(bs interface{}) int { - switch buf := bs.(type) { - case string: - return len(buf) - case []byte: - return len(buf) - } - panic("Can't get length: argument isn't a string nor []byte") -} - -func catchError(err *error) { - if pv := recover(); pv != nil { - switch e := pv.(type) { - case runtime.Error: - panic(pv) - case error: - *err = e - default: - panic(pv) - } - } -} diff --git a/src/github.com/ziutek/mymysql/native/consts.go b/src/github.com/ziutek/mymysql/native/consts.go deleted file mode 100644 index 908d918..0000000 --- a/src/github.com/ziutek/mymysql/native/consts.go +++ /dev/null @@ -1,192 +0,0 @@ -package native - -import "strconv" - -// Client caps - borrowed from GoMySQL -const ( - _CLIENT_LONG_PASSWORD = 1 << iota - _CLIENT_FOUND_ROWS - _CLIENT_LONG_FLAG - _CLIENT_CONNECT_WITH_DB - _CLIENT_NO_SCHEMA - _CLIENT_COMPRESS - _CLIENT_ODBC - _CLIENT_LOCAL_FILES - _CLIENT_IGNORE_SPACE - _CLIENT_PROTOCOL_41 - _CLIENT_INTERACTIVE - _CLIENT_SSL - _CLIENT_IGNORE_SIGPIPE - _CLIENT_TRANSACTIONS - _CLIENT_RESERVED - _CLIENT_SECURE_CONN - _CLIENT_MULTI_STATEMENTS - _CLIENT_MULTI_RESULTS -) - -// Commands - borrowed from GoMySQL -const ( - _COM_QUIT = 0x01 - _COM_INIT_DB = 0x02 - _COM_QUERY = 0x03 - _COM_FIELD_LIST = 0x04 - _COM_CREATE_DB = 0x05 - _COM_DROP_DB = 0x06 - _COM_REFRESH = 0x07 - _COM_SHUTDOWN = 0x08 - _COM_STATISTICS = 0x09 - _COM_PROCESS_INFO = 0x0a - _COM_CONNECT = 0x0b - _COM_PROCESS_KILL = 0x0c - _COM_DEBUG = 0x0d - _COM_PING = 0x0e - _COM_TIME = 0x0f - _COM_DELAYED_INSERT = 0x10 - _COM_CHANGE_USER = 0x11 - _COM_BINLOG_DUMP = 0x12 - _COM_TABLE_DUMP = 0x13 - _COM_CONNECT_OUT = 0x14 - _COM_REGISTER_SLAVE = 0x15 - _COM_STMT_PREPARE = 0x16 - _COM_STMT_EXECUTE = 0x17 - _COM_STMT_SEND_LONG_DATA = 0x18 - _COM_STMT_CLOSE = 0x19 - _COM_STMT_RESET = 0x1a - _COM_SET_OPTION = 0x1b - _COM_STMT_FETCH = 0x1c -) - -// Server status -const ( - _SERVER_STATUS_IN_TRANS = 0x01 // Transaction has started - _SERVER_STATUS_AUTOCOMMIT = 0x02 // Server in auto_commit mode - _SERVER_STATUS_MORE_RESULTS = 0x04 - _SERVER_MORE_RESULTS_EXISTS = 0x08 // Multi query - next query exists - _SERVER_QUERY_NO_GOOD_INDEX_USED = 0x10 - _SERVER_QUERY_NO_INDEX_USED = 0x20 - // Server opened a read-only non-scrollable cursor for a query - _SERVER_STATUS_CURSOR_EXISTS = 0x40 - _SERVER_STATUS_LAST_ROW_SENT = 0x80 - - _SERVER_STATUS_DB_DROPPED = 0x100 - _SERVER_STATUS_NO_BACKSLASH_ESCAPES = 0x200 -) - -// MySQL protocol types. -// -// mymysql uses only some of them for send data to the MySQL server. Used -// MySQL types are marked with a comment contains mymysql type that uses it. -const ( - MYSQL_TYPE_DECIMAL = 0x00 - MYSQL_TYPE_TINY = 0x01 // int8, uint8, bool - MYSQL_TYPE_SHORT = 0x02 // int16, uint16 - MYSQL_TYPE_LONG = 0x03 // int32, uint32 - MYSQL_TYPE_FLOAT = 0x04 // float32 - MYSQL_TYPE_DOUBLE = 0x05 // float64 - MYSQL_TYPE_NULL = 0x06 // nil - MYSQL_TYPE_TIMESTAMP = 0x07 // Timestamp - MYSQL_TYPE_LONGLONG = 0x08 // int64, uint64 - MYSQL_TYPE_INT24 = 0x09 - MYSQL_TYPE_DATE = 0x0a // Date - MYSQL_TYPE_TIME = 0x0b // Time - MYSQL_TYPE_DATETIME = 0x0c // time.Time - MYSQL_TYPE_YEAR = 0x0d - MYSQL_TYPE_NEWDATE = 0x0e - MYSQL_TYPE_VARCHAR = 0x0f - MYSQL_TYPE_BIT = 0x10 - MYSQL_TYPE_NEWDECIMAL = 0xf6 - MYSQL_TYPE_ENUM = 0xf7 - MYSQL_TYPE_SET = 0xf8 - MYSQL_TYPE_TINY_BLOB = 0xf9 - MYSQL_TYPE_MEDIUM_BLOB = 0xfa - MYSQL_TYPE_LONG_BLOB = 0xfb - MYSQL_TYPE_BLOB = 0xfc // Blob - MYSQL_TYPE_VAR_STRING = 0xfd // []byte - MYSQL_TYPE_STRING = 0xfe // string - MYSQL_TYPE_GEOMETRY = 0xff - - MYSQL_UNSIGNED_MASK = uint16(1 << 15) -) - -// Mapping of MySQL types to (prefered) protocol types. Use it if you create -// your own Raw value. -// -// Comments contains corresponding types used by mymysql. string type may be -// replaced by []byte type and vice versa. []byte type is native for sending -// on a network, so any string is converted to it before sending. Than for -// better preformance use []byte. -const ( - // Client send and receive, mymysql representation for send / receive - TINYINT = MYSQL_TYPE_TINY // int8 / int8 - SMALLINT = MYSQL_TYPE_SHORT // int16 / int16 - INT = MYSQL_TYPE_LONG // int32 / int32 - BIGINT = MYSQL_TYPE_LONGLONG // int64 / int64 - FLOAT = MYSQL_TYPE_FLOAT // float32 / float32 - DOUBLE = MYSQL_TYPE_DOUBLE // float64 / float32 - TIME = MYSQL_TYPE_TIME // Time / Time - DATE = MYSQL_TYPE_DATE // Date / Date - DATETIME = MYSQL_TYPE_DATETIME // time.Time / time.Time - TIMESTAMP = MYSQL_TYPE_TIMESTAMP // Timestamp / time.Time - CHAR = MYSQL_TYPE_STRING // string / []byte - BLOB = MYSQL_TYPE_BLOB // Blob / []byte - NULL = MYSQL_TYPE_NULL // nil - - // Client send only, mymysql representation for send - OUT_TEXT = MYSQL_TYPE_STRING // string - OUT_VARCHAR = MYSQL_TYPE_STRING // string - OUT_BINARY = MYSQL_TYPE_BLOB // Blob - OUT_VARBINARY = MYSQL_TYPE_BLOB // Blob - - // Client receive only, mymysql representation for receive - IN_MEDIUMINT = MYSQL_TYPE_INT24 // int32 - IN_YEAR = MYSQL_TYPE_SHORT // int16 - IN_BINARY = MYSQL_TYPE_STRING // []byte - IN_VARCHAR = MYSQL_TYPE_VAR_STRING // []byte - IN_VARBINARY = MYSQL_TYPE_VAR_STRING // []byte - IN_TINYBLOB = MYSQL_TYPE_TINY_BLOB // []byte - IN_TINYTEXT = MYSQL_TYPE_TINY_BLOB // []byte - IN_TEXT = MYSQL_TYPE_BLOB // []byte - IN_MEDIUMBLOB = MYSQL_TYPE_MEDIUM_BLOB // []byte - IN_MEDIUMTEXT = MYSQL_TYPE_MEDIUM_BLOB // []byte - IN_LONGBLOB = MYSQL_TYPE_LONG_BLOB // []byte - IN_LONGTEXT = MYSQL_TYPE_LONG_BLOB // []byte - - // MySQL 5.x specific - IN_DECIMAL = MYSQL_TYPE_NEWDECIMAL // TODO - IN_BIT = MYSQL_TYPE_BIT // []byte -) - -// Flags - borrowed from GoMySQL -const ( - _FLAG_NOT_NULL = 1 << iota - _FLAG_PRI_KEY - _FLAG_UNIQUE_KEY - _FLAG_MULTIPLE_KEY - _FLAG_BLOB - _FLAG_UNSIGNED - _FLAG_ZEROFILL - _FLAG_BINARY - _FLAG_ENUM - _FLAG_AUTO_INCREMENT - _FLAG_TIMESTAMP - _FLAG_SET - _FLAG_NO_DEFAULT_VALUE -) - -var ( - _SIZE_OF_INT int - _INT_TYPE uint16 -) - -func init() { - switch strconv.IntSize { - case 32: - _INT_TYPE = MYSQL_TYPE_LONG - _SIZE_OF_INT = 4 - case 64: - _INT_TYPE = MYSQL_TYPE_LONGLONG - _SIZE_OF_INT = 8 - default: - panic("bad int size") - } -} diff --git a/src/github.com/ziutek/mymysql/native/errors.go b/src/github.com/ziutek/mymysql/native/errors.go deleted file mode 100644 index d52cef5..0000000 --- a/src/github.com/ziutek/mymysql/native/errors.go +++ /dev/null @@ -1,31 +0,0 @@ -package native - -import ( - "errors" -) - -var ( - SEQ_ERROR = errors.New("packet sequence error") - PKT_ERROR = errors.New("malformed packet") - PKT_LONG_ERROR = errors.New("packet too long") - UNEXP_NULL_LCS_ERROR = errors.New("unexpected NULL LCS") - UNEXP_NULL_LCB_ERROR = errors.New("unexpected NULL LCB") - UNEXP_NULL_DATE_ERROR = errors.New("unexpected NULL DATETIME") - UNEXP_NULL_TIME_ERROR = errors.New("unexpected NULL TIME") - UNK_RESULT_PKT_ERROR = errors.New("unexpected or unknown result packet") - NOT_CONN_ERROR = errors.New("not connected") - ALREDY_CONN_ERROR = errors.New("not connected") - BAD_RESULT_ERROR = errors.New("unexpected result") - UNREADED_REPLY_ERROR = errors.New("reply is not completely read") - BIND_COUNT_ERROR = errors.New("wrong number of values for bind") - BIND_UNK_TYPE = errors.New("unknown value type for bind") - RESULT_COUNT_ERROR = errors.New("wrong number of result columns") - BAD_COMMAND_ERROR = errors.New("comand isn't text SQL nor *Stmt") - WRONG_DATE_LEN_ERROR = errors.New("wrong datetime/timestamp length") - WRONG_TIME_LEN_ERROR = errors.New("wrong time length") - UNK_MYSQL_TYPE_ERROR = errors.New("unknown MySQL type") - WRONG_PARAM_NUM_ERROR = errors.New("wrong parameter number") - UNK_DATA_TYPE_ERROR = errors.New("unknown data source type") - SMALL_PKT_SIZE_ERROR = errors.New("specified packet size is to small") - READ_AFTER_EOR_ERROR = errors.New("previous GetRow call returned nil row") -) diff --git a/src/github.com/ziutek/mymysql/native/init.go b/src/github.com/ziutek/mymysql/native/init.go deleted file mode 100644 index d145876..0000000 --- a/src/github.com/ziutek/mymysql/native/init.go +++ /dev/null @@ -1,65 +0,0 @@ -package native - -import ( - "log" -) - -func (my *Conn) init() { - my.seq = 0 // Reset sequence number, mainly for reconnect - if my.Debug { - log.Printf("[%2d ->] Init packet:", my.seq) - } - pr := my.newPktReader() - my.info.scramble = make([]byte, 20) - - my.info.prot_ver = readByte(pr) - my.info.serv_ver = readNTS(pr) - my.info.thr_id = readU32(pr) - readFull(pr, my.info.scramble[0:8]) - read(pr, 1) - my.info.caps = readU16(pr) - my.info.lang = readByte(pr) - my.status = readU16(pr) - read(pr, 13) - readFull(pr, my.info.scramble[8:]) - // Skip other information - pr.readAll() - - if my.Debug { - log.Printf(tab8s+"ProtVer=%d, ServVer=\"%s\" Status=0x%x", - my.info.prot_ver, my.info.serv_ver, my.status, - ) - } -} - -func (my *Conn) auth() { - if my.Debug { - log.Printf("[%2d <-] Authentication packet", my.seq) - } - pay_len := 4 + 4 + 1 + 23 + len(my.user) + 1 + 1 + len(my.info.scramble) - flags := uint32( - _CLIENT_PROTOCOL_41 | - _CLIENT_LONG_PASSWORD | - _CLIENT_SECURE_CONN | - _CLIENT_MULTI_STATEMENTS | - _CLIENT_MULTI_RESULTS | - _CLIENT_TRANSACTIONS, - ) - if len(my.dbname) > 0 { - pay_len += len(my.dbname) + 1 - flags |= _CLIENT_CONNECT_WITH_DB - } - encr_passwd := my.encryptedPasswd() - - pw := my.newPktWriter(pay_len) - writeU32(pw, flags) - writeU32(pw, uint32(my.max_pkt_size)) - writeByte(pw, my.info.lang) // Charset number - write(pw, make([]byte, 23)) // Filler - writeNTS(pw, my.user) // Username - writeBin(pw, encr_passwd) // Encrypted password - if len(my.dbname) > 0 { - writeNTS(pw, my.dbname) - } - return -} diff --git a/src/github.com/ziutek/mymysql/native/mysql.go b/src/github.com/ziutek/mymysql/native/mysql.go deleted file mode 100644 index f9d3f61..0000000 --- a/src/github.com/ziutek/mymysql/native/mysql.go +++ /dev/null @@ -1,729 +0,0 @@ -// Thread unsafe engine for MyMySQL -package native - -import ( - "bufio" - "fmt" - "github.com/ziutek/mymysql/mysql" - "io" - "net" - "reflect" -) - -type serverInfo struct { - prot_ver byte - serv_ver string - thr_id uint32 - scramble []byte - caps uint16 - lang byte -} - -// MySQL connection handler -type Conn struct { - proto string // Network protocol - laddr string // Local address - raddr string // Remote (server) address - - user string // MySQL username - passwd string // MySQL password - dbname string // Database name - - net_conn net.Conn // MySQL connection - rd *bufio.Reader - wr *bufio.Writer - - info serverInfo // MySQL server information - seq byte // MySQL sequence number - - unreaded_reply bool - - init_cmds []string // MySQL commands/queries executed after connect - stmt_map map[uint32]*Stmt // For reprepare during reconnect - - // Current status of MySQL server connection - status uint16 - - // Maximum packet size that client can accept from server. - // Default 16*1024*1024-1. You may change it before connect. - max_pkt_size int - - // Debug logging. You may change it at any time. - Debug bool -} - -// Create new MySQL handler. The first three arguments are passed to net.Bind -// for create connection. user and passwd are for authentication. Optional db -// is database name (you may not specifi it and use Use() method later). -func New(proto, laddr, raddr, user, passwd string, db ...string) mysql.Conn { - my := Conn{ - proto: proto, - laddr: laddr, - raddr: raddr, - user: user, - passwd: passwd, - stmt_map: make(map[uint32]*Stmt), - max_pkt_size: 16*1024*1024 - 1, - } - if len(db) == 1 { - my.dbname = db[0] - } else if len(db) > 1 { - panic("mymy.New: too many arguments") - } - return &my -} - -// If new_size > 0 sets maximum packet size. Returns old size. -func (my *Conn) SetMaxPktSize(new_size int) int { - old_size := my.max_pkt_size - if new_size > 0 { - my.max_pkt_size = new_size - } - return old_size -} - -func (my *Conn) connect() (err error) { - defer catchError(&err) - - // Make connection - switch my.proto { - case "tcp", "tcp4", "tcp6": - var la, ra *net.TCPAddr - if my.laddr != "" { - if la, err = net.ResolveTCPAddr("", my.laddr); err != nil { - return - } - } - if my.raddr != "" { - if ra, err = net.ResolveTCPAddr("", my.raddr); err != nil { - return - } - } - if my.net_conn, err = net.DialTCP(my.proto, la, ra); err != nil { - return - } - - case "unix": - var la, ra *net.UnixAddr - if my.raddr != "" { - if ra, err = net.ResolveUnixAddr(my.proto, my.raddr); err != nil { - return - } - } - if my.laddr != "" { - if la, err = net.ResolveUnixAddr(my.proto, my.laddr); err != nil { - return - } - } - if my.net_conn, err = net.DialUnix(my.proto, la, ra); err != nil { - return - } - - default: - err = net.UnknownNetworkError(my.proto) - } - - my.rd = bufio.NewReader(my.net_conn) - my.wr = bufio.NewWriter(my.net_conn) - - // Initialisation - my.init() - my.auth() - my.getResult(nil) - - // Execute all registered commands - for _, cmd := range my.init_cmds { - // Send command - my.sendCmd(_COM_QUERY, cmd) - // Get command response - res := my.getResponse() - - if res.field_count == 0 { - // No fields in result (OK result) - continue - } - // Read and discard all result rows - var row mysql.Row - for { - row, err = res.getRow() - if err != nil { - return - } - if row == nil { - res, err = res.nextResult() - if err != nil { - return - } - if res == nil { - // No more rows and results from this cmd - break - } - } - } - } - - return -} - -// Establishes a connection with MySQL server version 4.1 or later. -func (my *Conn) Connect() (err error) { - if my.net_conn != nil { - return ALREDY_CONN_ERROR - } - - return my.connect() -} - -// Check if connection is established -func (my *Conn) IsConnected() bool { - return my.net_conn != nil -} - -func (my *Conn) closeConn() (err error) { - defer catchError(&err) - - // Always close and invalidate connection, even if - // COM_QUIT returns an error - defer func() { - err = my.net_conn.Close() - my.net_conn = nil // Mark that we disconnect - }() - - // Close the connection - my.sendCmd(_COM_QUIT) - return -} - -// Close connection to the server -func (my *Conn) Close() (err error) { - if my.net_conn == nil { - return NOT_CONN_ERROR - } - if my.unreaded_reply { - return UNREADED_REPLY_ERROR - } - - return my.closeConn() -} - -// Close and reopen connection. -// Ignore unreaded rows, reprepare all prepared statements. -func (my *Conn) Reconnect() (err error) { - if my.net_conn != nil { - // Close connection, ignore all errors - my.closeConn() - } - // Reopen the connection. - if err = my.connect(); err != nil { - return - } - - // Reprepare all prepared statements - var ( - new_stmt *Stmt - new_map = make(map[uint32]*Stmt) - ) - for _, stmt := range my.stmt_map { - new_stmt, err = my.prepare(stmt.sql) - if err != nil { - return - } - // Assume that fields set in new_stmt by prepare() are indentical to - // corresponding fields in stmt. Why can they be different? - stmt.id = new_stmt.id - stmt.rebind = true - new_map[stmt.id] = stmt - } - // Replace the stmt_map - my.stmt_map = new_map - - return -} - -// Change database -func (my *Conn) Use(dbname string) (err error) { - defer catchError(&err) - - if my.net_conn == nil { - return NOT_CONN_ERROR - } - if my.unreaded_reply { - return UNREADED_REPLY_ERROR - } - - // Send command - my.sendCmd(_COM_INIT_DB, dbname) - // Get server response - my.getResult(nil) - // Save new database name if no errors - my.dbname = dbname - - return -} - -func (my *Conn) getResponse() (res *Result) { - res, ok := my.getResult(nil).(*Result) - if !ok { - panic(BAD_RESULT_ERROR) - } - if res.field_count != 0 { - // This query can return rows (this isn't OK result) - my.unreaded_reply = true - } - return -} - -// Start new query. -// -// If you specify the parameters, the SQL string will be a result of -// fmt.Sprintf(sql, params...). -// You must get all result rows (if they exists) before next query. -func (my *Conn) Start(sql string, params ...interface{}) (res mysql.Result, err error) { - defer catchError(&err) - - if my.net_conn == nil { - return nil, NOT_CONN_ERROR - } - if my.unreaded_reply { - return nil, UNREADED_REPLY_ERROR - } - - if len(params) != 0 { - sql = fmt.Sprintf(sql, params...) - } - // Send query - my.sendCmd(_COM_QUERY, sql) - - // Get command response - res = my.getResponse() - return -} - -func (res *Result) getRow() (row mysql.Row, err error) { - defer catchError(&err) - - switch result := res.my.getResult(res).(type) { - case mysql.Row: - // Row of data - row = result - - case *Result: - // EOF result - - default: - err = BAD_RESULT_ERROR - } - return -} - -func (res *Result) MoreResults() bool { - return res.status&_SERVER_MORE_RESULTS_EXISTS != 0 -} - -// Get the data row from server. This method reads one row of result directly -// from network connection (without rows buffering on client side). -func (res *Result) GetRow() (row mysql.Row, err error) { - if res.eor_returned { - err = READ_AFTER_EOR_ERROR - return - } - if res.field_count == 0 { - // There is no fields in result (OK result) - res.eor_returned = true - return - } - row, err = res.getRow() - if err == nil && row == nil { - res.eor_returned = true - if !res.MoreResults() { - res.my.unreaded_reply = false - } - } - return -} - -func (res *Result) nextResult() (next *Result, err error) { - defer catchError(&err) - if res.MoreResults() { - next = res.my.getResponse() - } - return -} - -// This function is used when last query was the multi result query. -// Return the next result or nil if no more resuts exists. -func (res *Result) NextResult() (mysql.Result, error) { - res, err := res.nextResult() - return res, err -} - -// Send MySQL PING to the server. -func (my *Conn) Ping() (err error) { - defer catchError(&err) - - if my.net_conn == nil { - return NOT_CONN_ERROR - } - if my.unreaded_reply { - return UNREADED_REPLY_ERROR - } - - // Send command - my.sendCmd(_COM_PING) - // Get server response - my.getResult(nil) - - return -} - -func (my *Conn) prepare(sql string) (stmt *Stmt, err error) { - defer catchError(&err) - - // Send command - my.sendCmd(_COM_STMT_PREPARE, sql) - // Get server response - stmt, ok := my.getPrepareResult(nil).(*Stmt) - if !ok { - return nil, BAD_RESULT_ERROR - } - if len(stmt.params) > 0 { - // Get param fields - my.getPrepareResult(stmt) - } - if len(stmt.fields) > 0 { - // Get column fields - my.getPrepareResult(stmt) - } - return -} - -// Prepare server side statement. Return statement handler. -func (my *Conn) Prepare(sql string) (mysql.Stmt, error) { - if my.net_conn == nil { - return nil, NOT_CONN_ERROR - } - if my.unreaded_reply { - return nil, UNREADED_REPLY_ERROR - } - - stmt, err := my.prepare(sql) - if err != nil { - return nil, err - } - // Connect statement with database handler - my.stmt_map[stmt.id] = stmt - // Save SQL for reconnect - stmt.sql = sql - - return stmt, nil -} - -// Bind input data for the parameter markers in the SQL statement that was -// passed to Prepare. -// -// params may be a parameter list (slice), a struct or a pointer to the struct. -// A struct field can by value or pointer to value. A parameter (slice element) -// can be value, pointer to value or pointer to pointer to value. -// Values may be of the folowind types: intXX, uintXX, floatXX, bool, []byte, -// Blob, string, Time, Date, Time, Timestamp, Raw. -func (stmt *Stmt) Bind(params ...interface{}) { - stmt.rebind = true - - // Check for struct binding - if len(params) == 1 { - pval := reflect.ValueOf(params[0]) - kind := pval.Kind() - if kind == reflect.Ptr { - // Dereference pointer - pval = pval.Elem() - kind = pval.Kind() - } - typ := pval.Type() - if kind == reflect.Struct && - typ != timeType && - typ != dateType && - typ != timestampType && - typ != rawType { - // We have struct to bind - if pval.NumField() != stmt.param_count { - panic(BIND_COUNT_ERROR) - } - if !pval.CanAddr() { - // Make an addressable structure - v := reflect.New(pval.Type()).Elem() - v.Set(pval) - pval = v - } - for ii := 0; ii < stmt.param_count; ii++ { - stmt.params[ii] = bindValue(pval.Field(ii)) - } - return - } - - } - - // There isn't struct to bind - - if len(params) != stmt.param_count { - panic(BIND_COUNT_ERROR) - } - for ii, par := range params { - pval := reflect.ValueOf(par) - if pval.IsValid() { - if pval.Kind() == reflect.Ptr { - // Dereference pointer - this value i addressable - pval = pval.Elem() - } else { - // Make an addressable value - v := reflect.New(pval.Type()).Elem() - v.Set(pval) - pval = v - } - } - stmt.params[ii] = bindValue(pval) - } -} - -// Resets the previous parameter binding -func (stmt *Stmt) ResetParams() { - stmt.rebind = true - for ii := 0; ii < stmt.param_count; ii++ { - stmt.params[ii] = nil - } -} - -// Execute prepared statement. If statement requires parameters you may bind -// them first or specify directly. After this command you may use GetRow to -// retrieve data. -func (stmt *Stmt) Run(params ...interface{}) (res mysql.Result, err error) { - defer catchError(&err) - - if stmt.my.net_conn == nil { - return nil, NOT_CONN_ERROR - } - if stmt.my.unreaded_reply { - return nil, UNREADED_REPLY_ERROR - } - - // Bind parameters if any - if len(params) != 0 { - stmt.Bind(params...) - } else if len(stmt.params) != stmt.param_count { - panic(BIND_COUNT_ERROR) - } - - // Send EXEC command with binded parameters - stmt.sendCmdExec() - // Get response - r := stmt.my.getResponse() - r.binary = true - res = r - return -} - -// Destroy statement on server side. Client side handler is invalid after this -// command. -func (stmt *Stmt) Delete() (err error) { - defer catchError(&err) - - if stmt.my.net_conn == nil { - return NOT_CONN_ERROR - } - if stmt.my.unreaded_reply { - return UNREADED_REPLY_ERROR - } - - // Allways delete statement on client side, even if - // the command return an error. - defer func() { - // Delete statement from stmt_map - delete(stmt.my.stmt_map, stmt.id) - // Invalidate handler - *stmt = Stmt{} - }() - - // Send command - stmt.my.sendCmd(_COM_STMT_CLOSE, stmt.id) - return -} - -// Resets a prepared statement on server: data sent to the server, unbuffered -// result sets and current errors. -func (stmt *Stmt) Reset() (err error) { - defer catchError(&err) - - if stmt.my.net_conn == nil { - return NOT_CONN_ERROR - } - if stmt.my.unreaded_reply { - return UNREADED_REPLY_ERROR - } - - // Next exec must send type information. We set rebind flag regardless of - // whether the command succeeds or not. - stmt.rebind = true - // Send command - stmt.my.sendCmd(_COM_STMT_RESET, stmt.id) - // Get result - stmt.my.getResult(nil) - return -} - -// Send long data to MySQL server in chunks. -// You can call this method after Bind and before Exec. It can be called -// multiple times for one parameter to send TEXT or BLOB data in chunks. -// -// pnum - Parameter number to associate the data with. -// -// data - Data source string, []byte or io.Reader. -// -// pkt_size - It must be must be greater than 6 and less or equal to MySQL -// max_allowed_packet variable. You can obtain value of this variable -// using such query: SHOW variables WHERE Variable_name = 'max_allowed_packet' -// If data source is io.Reader then (pkt_size - 6) is size of a buffer that -// will be allocated for reading. -// -// If you have data source of type string or []byte in one piece you may -// properly set pkt_size and call this method once. If you have data in -// multiple pieces you can call this method multiple times. If data source is -// io.Reader you should properly set pkt_size. Data will be readed from -// io.Reader and send in pieces to the server until EOF. -func (stmt *Stmt) SendLongData(pnum int, data interface{}, pkt_size int) (err error) { - defer catchError(&err) - - if stmt.my.net_conn == nil { - return NOT_CONN_ERROR - } - if stmt.my.unreaded_reply { - return UNREADED_REPLY_ERROR - } - if pnum < 0 || pnum >= stmt.param_count { - return WRONG_PARAM_NUM_ERROR - } - if pkt_size -= 6; pkt_size < 0 { - return SMALL_PKT_SIZE_ERROR - } - - switch dd := data.(type) { - case io.Reader: - buf := make([]byte, pkt_size) - for { - nn, ee := io.ReadFull(dd, buf) - if ee == io.EOF { - return - } - if nn != 0 { - stmt.my.sendCmd( - _COM_STMT_SEND_LONG_DATA, - stmt.id, uint16(pnum), buf[0:nn], - ) - } - if ee == io.ErrUnexpectedEOF { - return - } else if ee != nil { - return ee - } - } - - case []byte: - for len(dd) > pkt_size { - stmt.my.sendCmd( - _COM_STMT_SEND_LONG_DATA, - stmt.id, uint16(pnum), dd[0:pkt_size], - ) - dd = dd[pkt_size:] - } - stmt.my.sendCmd(_COM_STMT_SEND_LONG_DATA, stmt.id, uint16(pnum), dd) - return - - case string: - for len(dd) > pkt_size { - stmt.my.sendCmd( - _COM_STMT_SEND_LONG_DATA, - stmt.id, uint16(pnum), dd[0:pkt_size], - ) - dd = dd[pkt_size:] - } - stmt.my.sendCmd(_COM_STMT_SEND_LONG_DATA, stmt.id, uint16(pnum), dd) - return - } - return UNK_DATA_TYPE_ERROR -} - -// Returns the thread ID of the current connection. -func (my *Conn) ThreadId() uint32 { - return my.info.thr_id -} - -// Register MySQL command/query to be executed immediately after connecting to -// the server. You may register multiple commands. They will be executed in -// the order of registration. Yhis method is mainly useful for reconnect. -func (my *Conn) Register(sql string) { - my.init_cmds = append(my.init_cmds, sql) -} - -// See mysql.Query -func (my *Conn) Query(sql string, params ...interface{}) ([]mysql.Row, mysql.Result, error) { - return mysql.Query(my, sql, params...) -} - -// See mysql.Exec -func (stmt *Stmt) Exec(params ...interface{}) ([]mysql.Row, mysql.Result, error) { - return mysql.Exec(stmt, params...) -} - -// See mysql.End -func (res *Result) End() error { - return mysql.End(res) -} - -// See mysql.GetRows -func (res *Result) GetRows() ([]mysql.Row, error) { - return mysql.GetRows(res) -} - -// Escapes special characters in the txt, so it is safe to place returned string -// to Query method. -func (my *Conn) EscapeString(txt string) string { - if my.status&_SERVER_STATUS_NO_BACKSLASH_ESCAPES != 0 { - return escapeQuotes(txt) - } - return escapeString(txt) -} - -type Transaction struct { - *Conn -} - -// Starts a new transaction -func (my *Conn) Begin() (mysql.Transaction, error) { - _, err := my.Start("START TRANSACTION") - return &Transaction{my}, err -} - -// Commit a transaction -func (tr Transaction) Commit() error { - _, err := tr.Start("COMMIT") - tr.Conn = nil // Invalidate this transaction - return err -} - -// Rollback a transaction -func (tr Transaction) Rollback() error { - _, err := tr.Start("ROLLBACK") - tr.Conn = nil // Invalidate this transaction - return err -} - -// Binds statement to the context of transaction. For native engine this is -// identity function. -func (tr Transaction) Do(st mysql.Stmt) mysql.Stmt { - if s, ok := st.(*Stmt); !ok || s.my != tr.Conn { - panic("Transaction and statement doesn't belong to the same connection") - } - return st -} - -func init() { - mysql.New = New -} diff --git a/src/github.com/ziutek/mymysql/native/native_test.go b/src/github.com/ziutek/mymysql/native/native_test.go deleted file mode 100644 index 83da077..0000000 --- a/src/github.com/ziutek/mymysql/native/native_test.go +++ /dev/null @@ -1,1041 +0,0 @@ -package native - -import ( - "bytes" - "fmt" - "github.com/ziutek/mymysql/mysql" - "io/ioutil" - "os" - "reflect" - "testing" - "time" -) - -var ( - my mysql.Conn - user = "testuser" - passwd = "TestPasswd9" - dbname = "test" - //conn = []string{"unix", "", "/var/run/mysqld/mysqld.sock"} - conn = []string{"tcp", "", "127.0.0.1:3306"} - debug = false -) - -type RowsResErr struct { - rows []mysql.Row - res mysql.Result - err error -} - -func query(sql string, params ...interface{}) *RowsResErr { - rows, res, err := my.Query(sql, params...) - return &RowsResErr{rows, res, err} -} - -func exec(stmt *Stmt, params ...interface{}) *RowsResErr { - rows, res, err := stmt.Exec(params...) - return &RowsResErr{rows, res, err} -} - -func checkErr(t *testing.T, err error, exp_err error) { - if err != exp_err { - if exp_err == nil { - t.Fatalf("Error: %v", err) - } else { - t.Fatalf("Error: %v\nExpected error: %v", err, exp_err) - } - } -} - -func checkWarnCount(t *testing.T, res_cnt, exp_cnt int) { - if res_cnt != exp_cnt { - t.Errorf("Warning count: res=%d exp=%d", res_cnt, exp_cnt) - rows, res, err := my.Query("show warnings") - if err != nil { - t.Fatal("Can't get warrnings from MySQL", err) - } - for _, row := range rows { - t.Errorf("%s: \"%s\"", row.Str(res.Map("Level")), - row.Str(res.Map("Message"))) - } - t.FailNow() - } -} - -func checkErrWarn(t *testing.T, res, exp *RowsResErr) { - checkErr(t, res.err, exp.err) - checkWarnCount(t, res.res.WarnCount(), exp.res.WarnCount()) -} - -func types(row mysql.Row) (tt []reflect.Type) { - tt = make([]reflect.Type, len(row)) - for ii, val := range row { - tt[ii] = reflect.TypeOf(val) - } - return -} - -func checkErrWarnRows(t *testing.T, res, exp *RowsResErr) { - checkErrWarn(t, res, exp) - if !reflect.DeepEqual(res.rows, exp.rows) { - rlen := len(res.rows) - elen := len(exp.rows) - t.Error("Rows are different!") - t.Errorf("len/cap: res=%d/%d exp=%d/%d", - rlen, cap(res.rows), elen, cap(exp.rows)) - max := rlen - if elen > max { - max = elen - } - for ii := 0; ii < max; ii++ { - if ii < len(res.rows) { - t.Errorf("%d: res type: %s", ii, types(res.rows[ii])) - } else { - t.Errorf("%d: res: ------", ii) - } - if ii < len(exp.rows) { - t.Errorf("%d: exp type: %s", ii, types(exp.rows[ii])) - } else { - t.Errorf("%d: exp: ------", ii) - } - if ii < len(res.rows) { - t.Error(" res: ", res.rows[ii]) - } - if ii < len(exp.rows) { - t.Error(" exp: ", exp.rows[ii]) - } - if ii < len(res.rows) { - t.Errorf(" res: %#v", res.rows[ii][2]) - } - if ii < len(exp.rows) { - t.Errorf(" exp: %#v", exp.rows[ii][2]) - } - } - t.FailNow() - } -} - -func checkResult(t *testing.T, res, exp *RowsResErr) { - checkErrWarnRows(t, res, exp) - if !reflect.DeepEqual(res.res, exp.res) { - t.Fatalf("Bad result:\nres=%+v\nexp=%+v", res.res, exp.res) - } -} - -func cmdOK(affected uint64, binary, eor bool) *RowsResErr { - return &RowsResErr{res: &Result{my: my.(*Conn), binary: binary, status: 0x2, - message: []byte{}, affected_rows: affected, eor_returned: eor}} -} - -func selectOK(rows []mysql.Row, binary bool) (exp *RowsResErr) { - exp = cmdOK(0, binary, true) - exp.rows = rows - return -} - -func myConnect(t *testing.T, with_dbname bool, max_pkt_size int) { - if with_dbname { - my = New(conn[0], conn[1], conn[2], user, passwd, dbname) - } else { - my = New(conn[0], conn[1], conn[2], user, passwd) - } - - if max_pkt_size != 0 { - my.SetMaxPktSize(max_pkt_size) - } - my.(*Conn).Debug = debug - - checkErr(t, my.Connect(), nil) - checkResult(t, query("set names utf8"), cmdOK(0, false, true)) -} - -func myClose(t *testing.T) { - checkErr(t, my.Close(), nil) -} - -// Text queries tests - -func TestUse(t *testing.T) { - myConnect(t, false, 0) - checkErr(t, my.Use(dbname), nil) - myClose(t) -} - -func TestPing(t *testing.T) { - myConnect(t, false, 0) - checkErr(t, my.Ping(), nil) - myClose(t) -} - -func TestQuery(t *testing.T) { - myConnect(t, true, 0) - query("drop table T") // Drop test table if exists - checkResult(t, query("create table T (s varchar(40))"), - cmdOK(0, false, true)) - - exp := &RowsResErr{ - res: &Result{ - my: my.(*Conn), - field_count: 1, - fields: []*mysql.Field{ - &mysql.Field{ - Catalog: "def", - Db: "test", - Table: "Test", - OrgTable: "T", - Name: "Str", - OrgName: "s", - DispLen: 3 * 40, //varchar(40) - Flags: 0, - Type: MYSQL_TYPE_VAR_STRING, - Scale: 0, - }, - }, - fc_map: map[string]int{"Str": 0}, - status: _SERVER_STATUS_AUTOCOMMIT, - eor_returned: true, - }, - } - - for ii := 0; ii > 10000; ii += 3 { - var val interface{} - if ii%10 == 0 { - checkResult(t, query("insert T values (null)"), - cmdOK(1, false, true)) - val = nil - } else { - txt := []byte(fmt.Sprintf("%d %d %d %d %d", ii, ii, ii, ii, ii)) - checkResult(t, - query("insert T values ('%s')", txt), cmdOK(1, false, true)) - val = txt - } - exp.rows = append(exp.rows, mysql.Row{val}) - } - - checkResult(t, query("select s as Str from T as Test"), exp) - checkResult(t, query("drop table T"), cmdOK(0, false, true)) - myClose(t) -} - -// Prepared statements tests - -type StmtErr struct { - stmt *Stmt - err error -} - -func prepare(sql string) *StmtErr { - stmt, err := my.Prepare(sql) - return &StmtErr{stmt.(*Stmt), err} -} - -func checkStmt(t *testing.T, res, exp *StmtErr) { - ok := res.err == exp.err && - // Skipping id - reflect.DeepEqual(res.stmt.fields, exp.stmt.fields) && - reflect.DeepEqual(res.stmt.fc_map, exp.stmt.fc_map) && - res.stmt.field_count == exp.stmt.field_count && - res.stmt.param_count == exp.stmt.param_count && - res.stmt.warning_count == exp.stmt.warning_count && - res.stmt.status == exp.stmt.status - - if !ok { - if exp.err == nil { - checkErr(t, res.err, nil) - checkWarnCount(t, res.stmt.warning_count, exp.stmt.warning_count) - for _, v := range res.stmt.fields { - fmt.Printf("%+v\n", v) - } - t.Fatalf("Bad result statement: res=%v exp=%v", res.stmt, exp.stmt) - } - } -} - -func TestPrepared(t *testing.T) { - myConnect(t, true, 0) - query("drop table P") // Drop test table if exists - checkResult(t, - query( - "create table P ("+ - " ii int not null, ss varchar(20), dd datetime"+ - ") default charset=utf8", - ), - cmdOK(0, false, true), - ) - - exp := Stmt{ - fields: []*mysql.Field{ - &mysql.Field{ - Catalog: "def", Db: "test", Table: "P", OrgTable: "P", - Name: "i", - OrgName: "ii", - DispLen: 11, - Flags: _FLAG_NO_DEFAULT_VALUE | _FLAG_NOT_NULL, - Type: MYSQL_TYPE_LONG, - Scale: 0, - }, - &mysql.Field{ - Catalog: "def", Db: "test", Table: "P", OrgTable: "P", - Name: "s", - OrgName: "ss", - DispLen: 3 * 20, // varchar(20) - Flags: 0, - Type: MYSQL_TYPE_VAR_STRING, - Scale: 0, - }, - &mysql.Field{ - Catalog: "def", Db: "test", Table: "P", OrgTable: "P", - Name: "d", - OrgName: "dd", - DispLen: 19, - Flags: _FLAG_BINARY, - Type: MYSQL_TYPE_DATETIME, - Scale: 0, - }, - }, - fc_map: map[string]int{"i": 0, "s": 1, "d": 2}, - field_count: 3, - param_count: 2, - warning_count: 0, - status: 0x2, - } - - sel := prepare("select ii i, ss s, dd d from P where ii = ? and ss = ?") - checkStmt(t, sel, &StmtErr{&exp, nil}) - - all := prepare("select * from P") - checkErr(t, all.err, nil) - - ins := prepare("insert into P values (?, ?, ?)") - checkErr(t, ins.err, nil) - - parsed, err := mysql.ParseTime("2012-01-17 01:10:10", time.Local) - checkErr(t, err, nil) - parsedZero, err := mysql.ParseTime("0000-00-00 00:00:00", time.Local) - checkErr(t, err, nil) - if !parsedZero.IsZero() { - t.Fatalf("time '%s' isn't zero", parsedZero) - } - exp_rows := []mysql.Row{ - mysql.Row{ - 2, "Taki tekst", time.Unix(123456789, 0), - }, - mysql.Row{ - 5, "Pąk róży", parsed, - }, - mysql.Row{ - 11, "Zero UTC datetime", time.Unix(0, 0), - }, - mysql.Row{ - 17, mysql.Blob([]byte("Zero datetime")), parsedZero, - }, - mysql.Row{ - 23, []byte("NULL datetime"), (*time.Time)(nil), - }, - mysql.Row{ - 23, "NULL", nil, - }, - } - - for _, row := range exp_rows { - checkErrWarn(t, - exec(ins.stmt, row[0], row[1], row[2]), - cmdOK(1, true, true), - ) - } - - // Convert values to expected result types - for _, row := range exp_rows { - for ii, col := range row { - val := reflect.ValueOf(col) - // Dereference pointers - if val.Kind() == reflect.Ptr { - val = val.Elem() - } - switch val.Kind() { - case reflect.Invalid: - row[ii] = nil - - case reflect.String: - row[ii] = []byte(val.String()) - - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, - reflect.Int64: - row[ii] = int32(val.Int()) - - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, - reflect.Uint64: - row[ii] = int32(val.Uint()) - - case reflect.Slice: - if val.Type().Elem().Kind() == reflect.Uint8 { - bytes := make([]byte, val.Len()) - for ii := range bytes { - bytes[ii] = val.Index(ii).Interface().(uint8) - } - row[ii] = bytes - } - } - } - } - - checkErrWarn(t, exec(sel.stmt, 2, "Taki tekst"), selectOK(exp_rows, true)) - checkErrWarnRows(t, exec(all.stmt), selectOK(exp_rows, true)) - - checkResult(t, query("drop table P"), cmdOK(0, false, true)) - - checkErr(t, sel.stmt.Delete(), nil) - checkErr(t, all.stmt.Delete(), nil) - checkErr(t, ins.stmt.Delete(), nil) - - myClose(t) -} - -// Bind testing - -func TestVarBinding(t *testing.T) { - myConnect(t, true, 0) - query("drop table T") // Drop test table if exists - checkResult(t, - query("create table T (id int primary key, str varchar(20))"), - cmdOK(0, false, true), - ) - - ins, err := my.Prepare("insert T values (?, ?)") - checkErr(t, err, nil) - - var ( - rre RowsResErr - id *int - str *string - ii int - ss string - ) - ins.Bind(&id, &str) - - i1 := 1 - s1 := "Ala" - id = &i1 - str = &s1 - rre.res, rre.err = ins.Run() - checkResult(t, &rre, cmdOK(1, true, false)) - - i2 := 2 - s2 := "Ma kota!" - id = &i2 - str = &s2 - - rre.res, rre.err = ins.Run() - checkResult(t, &rre, cmdOK(1, true, false)) - - ins.Bind(&ii, &ss) - ii = 3 - ss = "A kot ma Ale!" - - rre.res, rre.err = ins.Run() - checkResult(t, &rre, cmdOK(1, true, false)) - - sel, err := my.Prepare("select str from T where id = ?") - checkErr(t, err, nil) - - rows, _, err := sel.Exec(1) - checkErr(t, err, nil) - if len(rows) != 1 || bytes.Compare([]byte(s1), rows[0].Bin(0)) != 0 { - t.Fatal("First string don't match") - } - - rows, _, err = sel.Exec(2) - checkErr(t, err, nil) - if len(rows) != 1 || bytes.Compare([]byte(s2), rows[0].Bin(0)) != 0 { - t.Fatal("Second string don't match") - } - - rows, _, err = sel.Exec(3) - checkErr(t, err, nil) - if len(rows) != 1 || bytes.Compare([]byte(ss), rows[0].Bin(0)) != 0 { - t.Fatal("Thrid string don't match") - } - - checkResult(t, query("drop table T"), cmdOK(0, false, true)) - myClose(t) -} - -func TestBindStruct(t *testing.T) { - myConnect(t, true, 0) - query("drop table T") // Drop test table if exists - checkResult(t, - query("create table T (id int primary key, txt varchar(20), b bool)"), - cmdOK(0, false, true), - ) - - ins, err := my.Prepare("insert T values (?, ?, ?)") - checkErr(t, err, nil) - sel, err := my.Prepare("select txt, b from T where id = ?") - checkErr(t, err, nil) - - var ( - s struct { - Id int - Txt string - B bool - } - rre RowsResErr - ) - - ins.Bind(&s) - - s.Id = 2 - s.Txt = "Ala ma kota." - s.B = true - - rre.res, rre.err = ins.Run() - checkResult(t, &rre, cmdOK(1, true, false)) - - rows, _, err := sel.Exec(s.Id) - checkErr(t, err, nil) - if len(rows) != 1 || rows[0].Str(0) != s.Txt || rows[0].Bool(1) != s.B { - t.Fatal("selected data don't match inserted data") - } - - checkResult(t, query("drop table T"), cmdOK(0, false, true)) - myClose(t) -} - -func TestDate(t *testing.T) { - myConnect(t, true, 0) - query("drop table D") // Drop test table if exists - checkResult(t, - query("create table D (id int, dd date, dt datetime, tt time)"), - cmdOK(0, false, true), - ) - - test := []struct { - dd, dt string - tt time.Duration - }{ - { - "2011-11-13", - "2010-12-12 11:24:00", - -time.Duration((128*3600 + 3*60 + 2) * 1e9), - }, { - "0000-00-00", - "0000-00-00 00:00:00", - time.Duration(0), - }, - } - - ins, err := my.Prepare("insert D values (?, ?, ?, ?)") - checkErr(t, err, nil) - - sel, err := my.Prepare("select id, tt from D where dd = ? && dt = ?") - checkErr(t, err, nil) - - for i, r := range test { - _, err = ins.Run(i, r.dd, r.dt, r.tt) - checkErr(t, err, nil) - - sdt, err := mysql.ParseTime(r.dt, time.Local) - checkErr(t, err, nil) - sdd, err := mysql.ParseDate(r.dd) - checkErr(t, err, nil) - - rows, _, err := sel.Exec(sdd, sdt) - checkErr(t, err, nil) - if rows == nil { - t.Fatal("nil result") - } - if rows[0].Int(0) != i { - t.Fatal("Bad id", rows[0].Int(1)) - } - if rows[0][1].(time.Duration) != r.tt { - t.Fatal("Bad tt", rows[0].Duration(1)) - } - } - - //checkResult(t, query("drop table D"), cmdOK(0, false, true)) - myClose(t) -} - -// Big blob -func TestBigBlob(t *testing.T) { - myConnect(t, true, 34*1024*1024) - query("drop table P") // Drop test table if exists - checkResult(t, - query("create table P (id int primary key, bb longblob)"), - cmdOK(0, false, true), - ) - - ins, err := my.Prepare("insert P values (?, ?)") - checkErr(t, err, nil) - - sel, err := my.Prepare("select bb from P where id = ?") - checkErr(t, err, nil) - - big_blob := make(mysql.Blob, 33*1024*1024) - for ii := range big_blob { - big_blob[ii] = byte(ii) - } - - var ( - rre RowsResErr - bb mysql.Blob - id int - ) - data := struct { - Id int - Bb mysql.Blob - }{} - - // Individual parameters binding - ins.Bind(&id, &bb) - id = 1 - bb = big_blob - - // Insert full blob. Three packets are sended. First two has maximum length - rre.res, rre.err = ins.Run() - checkResult(t, &rre, cmdOK(1, true, false)) - - // Struct binding - ins.Bind(&data) - data.Id = 2 - data.Bb = big_blob[0 : 32*1024*1024-31] - - // Insert part of blob - Two packets are sended. All has maximum length. - rre.res, rre.err = ins.Run() - checkResult(t, &rre, cmdOK(1, true, false)) - - sel.Bind(&id) - - // Check first insert. - tmr := "Too many rows" - - id = 1 - res, err := sel.Run() - checkErr(t, err, nil) - - row, err := res.GetRow() - checkErr(t, err, nil) - end, err := res.GetRow() - checkErr(t, err, nil) - if end != nil { - t.Fatal(tmr) - } - - if bytes.Compare(row[0].([]byte), big_blob) != 0 { - t.Fatal("Full blob data don't match") - } - - // Check second insert. - id = 2 - res, err = sel.Run() - checkErr(t, err, nil) - - row, err = res.GetRow() - checkErr(t, err, nil) - end, err = res.GetRow() - checkErr(t, err, nil) - if end != nil { - t.Fatal(tmr) - } - - if bytes.Compare(row.Bin(res.Map("bb")), data.Bb) != 0 { - t.Fatal("Partial blob data don't match") - } - - checkResult(t, query("drop table P"), cmdOK(0, false, true)) - myClose(t) -} - -// Test for empty result -func TestEmpty(t *testing.T) { - checkNil := func(r mysql.Row) { - if r != nil { - t.Error("Not empty result") - } - } - myConnect(t, true, 0) - query("drop table E") // Drop test table if exists - // Create table - checkResult(t, - query("create table E (id int)"), - cmdOK(0, false, true), - ) - // Text query - res, err := my.Start("select * from E") - checkErr(t, err, nil) - row, err := res.GetRow() - checkErr(t, err, nil) - checkNil(row) - row, err = res.GetRow() - checkErr(t, err, READ_AFTER_EOR_ERROR) - checkNil(row) - // Prepared statement - sel, err := my.Prepare("select * from E") - checkErr(t, err, nil) - res, err = sel.Run() - checkErr(t, err, nil) - row, err = res.GetRow() - checkErr(t, err, nil) - checkNil(row) - row, err = res.GetRow() - checkErr(t, err, READ_AFTER_EOR_ERROR) - checkNil(row) - // Drop test table - checkResult(t, query("drop table E"), cmdOK(0, false, true)) -} - -// Reconnect test -func TestReconnect(t *testing.T) { - myConnect(t, true, 0) - query("drop table R") // Drop test table if exists - checkResult(t, - query("create table R (id int primary key, str varchar(20))"), - cmdOK(0, false, true), - ) - - ins, err := my.Prepare("insert R values (?, ?)") - checkErr(t, err, nil) - sel, err := my.Prepare("select str from R where id = ?") - checkErr(t, err, nil) - - params := struct { - Id int - Str string - }{} - var sel_id int - - ins.Bind(¶ms) - sel.Bind(&sel_id) - - checkErr(t, my.Reconnect(), nil) - - params.Id = 1 - params.Str = "Bla bla bla" - _, err = ins.Run() - checkErr(t, err, nil) - - checkErr(t, my.Reconnect(), nil) - - sel_id = 1 - res, err := sel.Run() - checkErr(t, err, nil) - - row, err := res.GetRow() - checkErr(t, err, nil) - - checkErr(t, res.End(), nil) - - if row == nil || row[0] == nil || - params.Str != row.Str(0) { - t.Fatal("Bad result") - } - - checkErr(t, my.Reconnect(), nil) - - checkResult(t, query("drop table R"), cmdOK(0, false, true)) - myClose(t) -} - -// StmtSendLongData test - -func TestSendLongData(t *testing.T) { - myConnect(t, true, 64*1024*1024) - query("drop table L") // Drop test table if exists - checkResult(t, - query("create table L (id int primary key, bb longblob)"), - cmdOK(0, false, true), - ) - ins, err := my.Prepare("insert L values (?, ?)") - checkErr(t, err, nil) - - sel, err := my.Prepare("select bb from L where id = ?") - checkErr(t, err, nil) - - var ( - rre RowsResErr - id int64 - ) - - ins.Bind(&id, []byte(nil)) - sel.Bind(&id) - - // Prepare data - data := make([]byte, 4*1024*1024) - for ii := range data { - data[ii] = byte(ii) - } - // Send long data twice - checkErr(t, ins.SendLongData(1, data, 256*1024), nil) - checkErr(t, ins.SendLongData(1, data, 512*1024), nil) - - id = 1 - rre.res, rre.err = ins.Run() - checkResult(t, &rre, cmdOK(1, true, false)) - - res, err := sel.Run() - checkErr(t, err, nil) - - row, err := res.GetRow() - checkErr(t, err, nil) - - checkErr(t, res.End(), nil) - - if row == nil || row[0] == nil || - bytes.Compare(append(data, data...), row.Bin(0)) != 0 { - t.Fatal("Bad result") - } - - file, err := ioutil.TempFile("", "mymysql_test-") - checkErr(t, err, nil) - filename := file.Name() - defer os.Remove(filename) - - buf := make([]byte, 1024) - for i := 0; i < 2048; i++ { - _, err := file.Write(buf) - checkErr(t, err, nil) - } - checkErr(t, file.Close(), nil) - - // Send long data from io.Reader twice - file, err = os.Open(filename) - checkErr(t, err, nil) - checkErr(t, ins.SendLongData(1, file, 128*1024), nil) - checkErr(t, file.Close(), nil) - file, err = os.Open(filename) - checkErr(t, err, nil) - checkErr(t, ins.SendLongData(1, file, 1024*1024), nil) - checkErr(t, file.Close(), nil) - - id = 2 - rre.res, rre.err = ins.Run() - checkResult(t, &rre, cmdOK(1, true, false)) - - res, err = sel.Run() - checkErr(t, err, nil) - - row, err = res.GetRow() - checkErr(t, err, nil) - - checkErr(t, res.End(), nil) - - // Read file for check result - data, err = ioutil.ReadFile(filename) - checkErr(t, err, nil) - - if row == nil || row[0] == nil || - bytes.Compare(append(data, data...), row.Bin(0)) != 0 { - t.Fatal("Bad result") - } - - checkResult(t, query("drop table L"), cmdOK(0, false, true)) - myClose(t) -} - -func TestNull(t *testing.T) { - myConnect(t, true, 0) - query("drop table if exists N") - checkResult(t, - query("create table N (i int not null, n int)"), - cmdOK(0, false, true), - ) - ins, err := my.Prepare("insert N values (?, ?)") - checkErr(t, err, nil) - - var ( - p struct{ I, N *int } - rre RowsResErr - ) - ins.Bind(&p) - - p.I = new(int) - p.N = new(int) - - *p.I = 0 - *p.N = 1 - rre.res, rre.err = ins.Run() - checkResult(t, &rre, cmdOK(1, true, false)) - *p.I = 1 - p.N = nil - rre.res, rre.err = ins.Run() - checkResult(t, &rre, cmdOK(1, true, false)) - - checkResult(t, query("insert N values (2, 1)"), cmdOK(1, false, true)) - checkResult(t, query("insert N values (3, NULL)"), cmdOK(1, false, true)) - - rows, res, err := my.Query("select * from N") - checkErr(t, err, nil) - if len(rows) != 4 { - t.Fatal("str: len(rows) != 4") - } - i := res.Map("i") - n := res.Map("n") - for k, row := range rows { - switch { - case row[i] == nil || row.Int(i) != k: - case k%2 == 1 && row[n] != nil: - case k%2 == 0 && (row[n] == nil || row.Int(n) != 1): - default: - continue - } - t.Fatalf("str row: %d = (%s, %s)", k, row[i], row[n]) - } - - sel, err := my.Prepare("select * from N") - checkErr(t, err, nil) - rows, res, err = sel.Exec() - checkErr(t, err, nil) - if len(rows) != 4 { - t.Fatal("bin: len(rows) != 4") - } - i = res.Map("i") - n = res.Map("n") - for k, row := range rows { - switch { - case row[i] == nil || row.Int(i) != k: - case k%2 == 1 && row[n] != nil: - case k%2 == 0 && (row[n] == nil || row.Int(n) != 1): - default: - continue - } - t.Fatalf("bin row: %d = (%v, %v)", k, row[i], row[n]) - } - - checkResult(t, query("drop table N"), cmdOK(0, false, true)) -} - -func TestMultipleResults(t *testing.T) { - myConnect(t, true, 0) - query("drop table M") // Drop test table if exists - checkResult(t, - query("create table M (id int primary key, str varchar(20))"), - cmdOK(0, false, true), - ) - - str := []string{"zero", "jeden", "dwa"} - - checkResult(t, query("insert M values (0, '%s')", str[0]), - cmdOK(1, false, true)) - checkResult(t, query("insert M values (1, '%s')", str[1]), - cmdOK(1, false, true)) - checkResult(t, query("insert M values (2, '%s')", str[2]), - cmdOK(1, false, true)) - - res, err := my.Start("select id from M; select str from M") - checkErr(t, err, nil) - - for ii := 0; ; ii++ { - row, err := res.GetRow() - checkErr(t, err, nil) - if row == nil { - break - } - if row.Int(0) != ii { - t.Fatal("Bad result") - } - } - res, err = res.NextResult() - checkErr(t, err, nil) - for ii := 0; ; ii++ { - row, err := res.GetRow() - checkErr(t, err, nil) - if row == nil { - break - } - if row.Str(0) != str[ii] { - t.Fatal("Bad result") - } - } - - checkResult(t, query("drop table M"), cmdOK(0, false, true)) - myClose(t) -} - -// Benchamrks - -func check(err error) { - if err != nil { - fmt.Println(err) - os.Exit(1) - } -} - -func BenchmarkInsertSelect(b *testing.B) { - b.StopTimer() - - my := New(conn[0], conn[1], conn[2], user, passwd, dbname) - check(my.Connect()) - - my.Start("drop table B") // Drop test table if exists - - _, err := my.Start("create table B (s varchar(40), i int)") - check(err) - - for ii := 0; ii < 10000; ii++ { - _, err := my.Start("insert B values ('%d-%d-%d', %d)", ii, ii, ii, ii) - check(err) - } - - b.StartTimer() - - for ii := 0; ii < b.N; ii++ { - res, err := my.Start("select * from B") - check(err) - for { - row, err := res.GetRow() - check(err) - if row == nil { - break - } - } - } - - b.StopTimer() - - _, err = my.Start("drop table B") - check(err) - check(my.Close()) -} - -func BenchmarkPreparedInsertSelect(b *testing.B) { - b.StopTimer() - - my := New(conn[0], conn[1], conn[2], user, passwd, dbname) - check(my.Connect()) - - my.Start("drop table B") // Drop test table if exists - - _, err := my.Start("create table B (s varchar(40), i int)") - check(err) - - ins, err := my.Prepare("insert B values (?, ?)") - check(err) - - sel, err := my.Prepare("select * from B") - check(err) - - for ii := 0; ii < 10000; ii++ { - _, err := ins.Run(fmt.Sprintf("%d-%d-%d", ii, ii, ii), ii) - check(err) - } - - b.StartTimer() - - for ii := 0; ii < b.N; ii++ { - res, err := sel.Run() - check(err) - for { - row, err := res.GetRow() - check(err) - if row == nil { - break - } - } - } - - b.StopTimer() - - _, err = my.Start("drop table B") - check(err) - check(my.Close()) -} diff --git a/src/github.com/ziutek/mymysql/native/packet.go b/src/github.com/ziutek/mymysql/native/packet.go deleted file mode 100644 index bdd5be4..0000000 --- a/src/github.com/ziutek/mymysql/native/packet.go +++ /dev/null @@ -1,154 +0,0 @@ -package native - -import ( - "bufio" - "errors" - "io" -) - -type pktReader struct { - rd *bufio.Reader - seq *byte - remain int - last bool -} - -func (my *Conn) newPktReader() *pktReader { - return &pktReader{rd: my.rd, seq: &my.seq} -} - -func (pr *pktReader) Read(buf []byte) (num int, err error) { - if len(buf) == 0 { - return 0, nil - } - defer catchError(&err) - - if pr.remain == 0 { - // No data to read from current packet - if pr.last { - // No more packets - return 0, io.EOF - } - // Read next packet header - pr.remain = int(readU24(pr.rd)) - seq := readByte(pr.rd) - // Chceck sequence number - if *pr.seq != seq { - return 0, SEQ_ERROR - } - *pr.seq++ - // Last packet? - pr.last = (pr.remain != 0xffffff) - } - // Reading data - if len(buf) <= pr.remain { - num, err = pr.rd.Read(buf) - } else { - num, err = pr.rd.Read(buf[0:pr.remain]) - } - pr.remain -= num - return -} - -func (pr *pktReader) readAll() (buf []byte) { - buf = make([]byte, pr.remain) - nn := 0 - for { - readFull(pr, buf[nn:]) - if pr.last { - break - } - // There is next packet to read - new_buf := make([]byte, len(buf)+pr.remain) - copy(new_buf[nn:], buf) - nn += len(buf) - buf = new_buf - } - return -} - -func (pr *pktReader) unreadByte() { - if err := pr.rd.UnreadByte(); err != nil { - panic(err) - } - pr.remain++ -} - -func (pr *pktReader) eof() bool { - return pr.remain == 0 && pr.last -} - -func (pr *pktReader) checkEof() { - if !pr.eof() { - panic(PKT_LONG_ERROR) - } -} - -type pktWriter struct { - wr *bufio.Writer - seq *byte - remain int - to_write int - last bool -} - -func (my *Conn) newPktWriter(to_write int) *pktWriter { - return &pktWriter{wr: my.wr, seq: &my.seq, to_write: to_write} -} - -/*func writePktHeader(wr io.Writer, seq byte, pay_len int) { - writeU24(wr, uint32(pay_len)) - writeByte(wr, seq) -}*/ - -func (pw *pktWriter) Write(buf []byte) (num int, err error) { - if len(buf) == 0 { - return - } - defer catchError(&err) - - var nn int - for len(buf) != 0 { - if pw.remain == 0 { - if pw.to_write == 0 { - err = errors.New("too many data for write as packet") - return - } - if pw.to_write >= 0xffffff { - pw.remain = 0xffffff - } else { - pw.remain = pw.to_write - pw.last = true - } - pw.to_write -= pw.remain - // Write packet header - writeU24(pw.wr, uint32(pw.remain)) - writeByte(pw.wr, *pw.seq) - // Update sequence number - *pw.seq++ - } - nn = len(buf) - if nn > pw.remain { - nn = pw.remain - } - nn, err = pw.wr.Write(buf[0:nn]) - num += nn - pw.remain -= nn - if err != nil { - return - } - buf = buf[nn:] - } - if pw.remain+pw.to_write == 0 { - if !pw.last { - // Write header for empty packet - writeU24(pw.wr, 0) - writeByte(pw.wr, *pw.seq) - // Update sequence number - *pw.seq++ - } - // Flush bufio buffers - err = pw.wr.Flush() - } - return -} diff --git a/src/github.com/ziutek/mymysql/native/prepared.go b/src/github.com/ziutek/mymysql/native/prepared.go deleted file mode 100644 index 58e090c..0000000 --- a/src/github.com/ziutek/mymysql/native/prepared.go +++ /dev/null @@ -1,168 +0,0 @@ -package native - -import ( - "github.com/ziutek/mymysql/mysql" - "log" -) - -type Stmt struct { - my *Conn - - id uint32 - sql string // For reprepare during reconnect - - params []*paramValue // Parameters binding - rebind bool - - fields []*mysql.Field - fc_map map[string]int // Maps field name to column number - - field_count int - param_count int - warning_count int - status uint16 -} - -// Returns index for given name or -1 if field of that name doesn't exist -func (res *Stmt) Map(field_name string) int { - if fi, ok := res.fc_map[field_name]; ok { - return fi - } - return -1 -} - -func (stmt *Stmt) NumField() int { - return stmt.field_count -} - -func (stmt *Stmt) NumParam() int { - return stmt.param_count -} - -func (stmt *Stmt) WarnCount() int { - return stmt.warning_count -} - -func (stmt *Stmt) sendCmdExec() { - // Calculate packet length and NULL bitmap - null_bitmap := make([]byte, (stmt.param_count+7)>>3) - pkt_len := 1 + 4 + 1 + 4 + 1 + len(null_bitmap) - for ii, param := range stmt.params { - par_len := param.Len() - pkt_len += par_len - if par_len == 0 { - null_byte := ii >> 3 - null_mask := byte(1) << uint(ii-(null_byte<<3)) - null_bitmap[null_byte] |= null_mask - } - } - if stmt.rebind { - pkt_len += stmt.param_count * 2 - } - // Reset sequence number - stmt.my.seq = 0 - // Packet sending - pw := stmt.my.newPktWriter(pkt_len) - writeByte(pw, _COM_STMT_EXECUTE) - writeU32(pw, stmt.id) - writeByte(pw, 0) // flags = CURSOR_TYPE_NO_CURSOR - writeU32(pw, 1) // iteration_count - write(pw, null_bitmap) - if stmt.rebind { - writeByte(pw, 1) - // Types - for _, param := range stmt.params { - writeU16(pw, param.typ) - } - } else { - writeByte(pw, 0) - } - // Values - for _, param := range stmt.params { - writeValue(pw, param) - } - - if stmt.my.Debug { - log.Printf("[%2d <-] Exec command packet: len=%d, null_bitmap=%v, rebind=%t", - stmt.my.seq-1, pkt_len, null_bitmap, stmt.rebind) - } - - // Mark that we sended information about binded types - stmt.rebind = false -} - -func (my *Conn) getPrepareResult(stmt *Stmt) interface{} { -loop: - pr := my.newPktReader() // New reader for next packet - pkt0 := readByte(pr) - - //log.Println("pkt0:", pkt0, "stmt:", stmt) - - if pkt0 == 255 { - // Error packet - my.getErrorPacket(pr) - } - - if stmt == nil { - if pkt0 == 0 { - // OK packet - return my.getPrepareOkPacket(pr) - } - } else { - unreaded_params := (stmt.param_count < len(stmt.params)) - switch { - case pkt0 == 254: - // EOF packet - stmt.warning_count, stmt.status = my.getEofPacket(pr) - stmt.my.status = stmt.status - return stmt - - case pkt0 > 0 && pkt0 < 251 && (stmt.field_count < len(stmt.fields) || - unreaded_params): - // Field packet - if unreaded_params { - // Read and ignore parameter field. Sentence from MySQL source: - /* skip parameters data: we don't support it yet */ - my.getFieldPacket(pr) - // Increment field count - stmt.param_count++ - } else { - field := my.getFieldPacket(pr) - stmt.fields[stmt.field_count] = field - stmt.fc_map[field.Name] = stmt.field_count - // Increment field count - stmt.field_count++ - } - // Read next packet - goto loop - } - } - panic(UNK_RESULT_PKT_ERROR) -} - -func (my *Conn) getPrepareOkPacket(pr *pktReader) (stmt *Stmt) { - if my.Debug { - log.Printf("[%2d ->] Perpared OK packet:", my.seq-1) - } - - stmt = new(Stmt) - stmt.my = my - // First byte was readed by getPrepRes - stmt.id = readU32(pr) - stmt.fields = make([]*mysql.Field, int(readU16(pr))) // FieldCount - stmt.params = make([]*paramValue, int(readU16(pr))) // ParamCount - read(pr, 1) - stmt.warning_count = int(readU16(pr)) - pr.checkEof() - - // Make field map if fields exists. - if len(stmt.fields) > 0 { - stmt.fc_map = make(map[string]int) - } - if my.Debug { - log.Printf(tab8s+"ID=0x%x ParamCount=%d FieldsCount=%d WarnCount=%d", - stmt.id, len(stmt.params), len(stmt.fields), stmt.warning_count, - ) - } - return -} diff --git a/src/github.com/ziutek/mymysql/native/result.go b/src/github.com/ziutek/mymysql/native/result.go deleted file mode 100644 index 92e4b7a..0000000 --- a/src/github.com/ziutek/mymysql/native/result.go +++ /dev/null @@ -1,325 +0,0 @@ -package native - -import ( - "github.com/ziutek/mymysql/mysql" - "log" - "math" -) - -type Result struct { - my *Conn - binary bool // Binary result expected - - field_count int - fields []*mysql.Field // Fields table - fc_map map[string]int // Maps field name to column number - - message []byte - affected_rows uint64 - - // Primary key value (useful for AUTO_INCREMENT primary keys) - insert_id uint64 - - // Number of warinigs during command execution - // You can use the SHOW WARNINGS query for details. - warning_count int - - // MySQL server status immediately after the query execution - status uint16 - - // Seted by GetRow if it returns nil row - eor_returned bool -} - -func (res *Result) Fields() []*mysql.Field { - return res.fields -} - -// Returns index for given name or -1 if field of that name doesn't exist -func (res *Result) Map(field_name string) int { - if fi, ok := res.fc_map[field_name]; ok { - return fi - } - return -1 -} - -func (res *Result) Message() string { - return string(res.message) -} - -func (res *Result) AffectedRows() uint64 { - return res.affected_rows -} - -func (res *Result) InsertId() uint64 { - return res.insert_id -} - -func (res *Result) WarnCount() int { - return res.warning_count -} - -func (my *Conn) getResult(res *Result) interface{} { -loop: - pr := my.newPktReader() // New reader for next packet - pkt0 := readByte(pr) - - if pkt0 == 255 { - // Error packet - my.getErrorPacket(pr) - } - - if res == nil { - switch { - case pkt0 == 0: - // OK packet - return my.getOkPacket(pr) - - case pkt0 > 0 && pkt0 < 251: - // Result set header packet - res = my.getResSetHeadPacket(pr) - // Read next packet - goto loop - } - } else { - switch { - case pkt0 == 254: - // EOF packet - res.warning_count, res.status = my.getEofPacket(pr) - my.status = res.status - return res - - case pkt0 > 0 && pkt0 < 251 && res.field_count < len(res.fields): - // Field packet - field := my.getFieldPacket(pr) - res.fields[res.field_count] = field - res.fc_map[field.Name] = res.field_count - // Increment field count - res.field_count++ - // Read next packet - goto loop - - case pkt0 < 254 && res.field_count == len(res.fields): - // Row Data Packet - if res.binary { - return my.getBinRowPacket(pr, res) - } else { - return my.getTextRowPacket(pr, res) - } - } - } - panic(UNK_RESULT_PKT_ERROR) -} - -func (my *Conn) getOkPacket(pr *pktReader) (res *Result) { - if my.Debug { - log.Printf("[%2d ->] OK packet:", my.seq-1) - } - res = new(Result) - res.my = my - // First byte was readed by getResult - res.affected_rows = readLCB(pr) - res.insert_id = readLCB(pr) - res.status = readU16(pr) - my.status = res.status - res.warning_count = int(readU16(pr)) - res.message = pr.readAll() - pr.checkEof() - - if my.Debug { - log.Printf(tab8s+"AffectedRows=%d InsertId=0x%x Status=0x%x "+ - "WarningCount=%d Message=\"%s\"", res.affected_rows, res.insert_id, - res.status, res.warning_count, res.message, - ) - } - return -} - -func (my *Conn) getErrorPacket(pr *pktReader) { - if my.Debug { - log.Printf("[%2d ->] Error packet:", my.seq-1) - } - var err mysql.Error - err.Code = readU16(pr) - if readByte(pr) != '#' { - panic(PKT_ERROR) - } - read(pr, 5) - err.Msg = pr.readAll() - pr.checkEof() - - if my.Debug { - log.Printf(tab8s+"code=0x%x msg=\"%s\"", err.Code, err.Msg) - } - panic(&err) -} - -func (my *Conn) getEofPacket(pr *pktReader) (warn_count int, status uint16) { - if my.Debug { - log.Printf("[%2d ->] EOF packet:", my.seq-1) - } - warn_count = int(readU16(pr)) - status = readU16(pr) - pr.checkEof() - - if my.Debug { - log.Printf(tab8s+"WarningCount=%d Status=0x%x", warn_count, status) - } - return -} - -func (my *Conn) getResSetHeadPacket(pr *pktReader) (res *Result) { - if my.Debug { - log.Printf("[%2d ->] Result set header packet:", my.seq-1) - } - pr.unreadByte() - - field_count := int(readLCB(pr)) - pr.checkEof() - - res = &Result{ - my: my, - fields: make([]*mysql.Field, field_count), - fc_map: make(map[string]int), - } - - if my.Debug { - log.Printf(tab8s+"FieldCount=%d", field_count) - } - return -} - -func (my *Conn) getFieldPacket(pr *pktReader) (field *mysql.Field) { - if my.Debug { - log.Printf("[%2d ->] Field packet:", my.seq-1) - } - pr.unreadByte() - - field = new(mysql.Field) - field.Catalog = readStr(pr) - field.Db = readStr(pr) - field.Table = readStr(pr) - field.OrgTable = readStr(pr) - field.Name = readStr(pr) - field.OrgName = readStr(pr) - read(pr, 1+2) - //field.Charset= readU16(pr) - field.DispLen = readU32(pr) - field.Type = readByte(pr) - field.Flags = readU16(pr) - field.Scale = readByte(pr) - read(pr, 2) - pr.checkEof() - - if my.Debug { - log.Printf(tab8s+"Name=\"%s\" Type=0x%x", field.Name, field.Type) - } - return -} - -func (my *Conn) getTextRowPacket(pr *pktReader, res *Result) mysql.Row { - if my.Debug { - log.Printf("[%2d ->] Text row data packet", my.seq-1) - } - pr.unreadByte() - - row := make(mysql.Row, res.field_count) - for ii := 0; ii < res.field_count; ii++ { - bin, null := readNullBin(pr) - if null { - row[ii] = nil - } else { - row[ii] = bin - } - } - pr.checkEof() - - return row -} - -func (my *Conn) getBinRowPacket(pr *pktReader, res *Result) mysql.Row { - if my.Debug { - log.Printf("[%2d ->] Binary row data packet", my.seq-1) - } - // First byte was readed by getResult - - null_bitmap := make([]byte, (res.field_count+7+2)>>3) - readFull(pr, null_bitmap) - - row := make(mysql.Row, res.field_count) - for ii, field := range res.fields { - null_byte := (ii + 2) >> 3 - null_mask := byte(1) << uint(2+ii-(null_byte<<3)) - if null_bitmap[null_byte]&null_mask != 0 { - // Null field - row[ii] = nil - continue - } - typ := field.Type - unsigned := (field.Flags & _FLAG_UNSIGNED) != 0 - switch typ { - case MYSQL_TYPE_TINY: - if unsigned { - row[ii] = readByte(pr) - } else { - row[ii] = int8(readByte(pr)) - } - - case MYSQL_TYPE_SHORT: - if unsigned { - row[ii] = readU16(pr) - } else { - row[ii] = int16(readU16(pr)) - } - - case MYSQL_TYPE_LONG: - if unsigned { - row[ii] = readU32(pr) - } else { - row[ii] = int32(readU32(pr)) - } - - case MYSQL_TYPE_LONGLONG: - if unsigned { - row[ii] = readU64(pr) - } else { - row[ii] = int64(readU64(pr)) - } - - case MYSQL_TYPE_INT24: - if unsigned { - row[ii] = readU24(pr) - } else { - row[ii] = int32(readU24(pr)) - } - - case MYSQL_TYPE_FLOAT: - row[ii] = math.Float32frombits(readU32(pr)) - - case MYSQL_TYPE_DOUBLE: - row[ii] = math.Float64frombits(readU64(pr)) - - case MYSQL_TYPE_STRING, MYSQL_TYPE_VAR_STRING, MYSQL_TYPE_DECIMAL, - MYSQL_TYPE_VARCHAR, MYSQL_TYPE_BIT, MYSQL_TYPE_BLOB, - MYSQL_TYPE_TINY_BLOB, MYSQL_TYPE_MEDIUM_BLOB, - MYSQL_TYPE_LONG_BLOB, MYSQL_TYPE_SET, MYSQL_TYPE_ENUM: - row[ii] = readBin(pr) - - case MYSQL_TYPE_DATE: - row[ii] = readDate(pr) - - case MYSQL_TYPE_DATETIME, MYSQL_TYPE_TIMESTAMP: - row[ii] = readTime(pr) - - case MYSQL_TYPE_TIME: - row[ii] = readDuration(pr) - - // TODO: - // MYSQL_TYPE_NEWDATE, MYSQL_TYPE_NEWDECIMAL, MYSQL_TYPE_GEOMETRY - - default: - panic(UNK_MYSQL_TYPE_ERROR) - } - } - return row -} diff --git a/src/github.com/ziutek/mymysql/native/unsafe.go b/src/github.com/ziutek/mymysql/native/unsafe.go deleted file mode 100644 index 2c8f9b4..0000000 --- a/src/github.com/ziutek/mymysql/native/unsafe.go +++ /dev/null @@ -1,116 +0,0 @@ -package native - -import ( - "github.com/ziutek/mymysql/mysql" - "io" - "time" - "unsafe" -) - -type paramValue struct { - typ uint16 - addr unsafe.Pointer - raw bool - length int // >=0 - length of value, <0 - unknown length -} - -func (pv *paramValue) SetAddr(addr uintptr) { - pv.addr = unsafe.Pointer(addr) -} - -func (val *paramValue) Len() int { - if val.addr == nil { - // Invalid Value was binded - return 0 - } - // val.addr always points to the pointer - lets dereference it - ptr := *(*unsafe.Pointer)(val.addr) - if ptr == nil { - // Binded Ptr Value is nil - return 0 - } - - if val.length >= 0 { - return val.length - } - - switch val.typ { - case MYSQL_TYPE_STRING: - return lenStr(*(*string)(ptr)) - - case MYSQL_TYPE_DATE: - return lenDate(*(*mysql.Date)(ptr)) - - case MYSQL_TYPE_TIMESTAMP, MYSQL_TYPE_DATETIME: - return lenTime(*(*time.Time)(ptr)) - - case MYSQL_TYPE_TIME: - return lenDuration(*(*time.Duration)(ptr)) - - case MYSQL_TYPE_TINY: // val.length < 0 so this is bool - return 1 - } - // MYSQL_TYPE_VAR_STRING, MYSQL_TYPE_BLOB and type of Raw value - return lenBin(*(*[]byte)(ptr)) -} - -func writeValue(wr io.Writer, val *paramValue) { - if val.addr == nil { - // Invalid Value was binded - return - } - // val.addr always points to the pointer - lets dereference it - ptr := *(*unsafe.Pointer)(val.addr) - if ptr == nil { - // Binded Ptr Value is nil - return - } - - if val.raw || val.typ == MYSQL_TYPE_VAR_STRING || - val.typ == MYSQL_TYPE_BLOB { - writeBin(wr, *(*[]byte)(ptr)) - return - } - // We don't need unsigned bit to check type - switch val.typ & ^MYSQL_UNSIGNED_MASK { - case MYSQL_TYPE_NULL: - // Don't write null values - - case MYSQL_TYPE_STRING: - writeStr(wr, *(*string)(ptr)) - - case MYSQL_TYPE_LONG, MYSQL_TYPE_FLOAT: - writeU32(wr, *(*uint32)(ptr)) - - case MYSQL_TYPE_SHORT: - writeU16(wr, *(*uint16)(ptr)) - - case MYSQL_TYPE_TINY: - if val.length == -1 { - // Translate bool value to MySQL tiny - if *(*bool)(ptr) { - writeByte(wr, 1) - } else { - writeByte(wr, 0) - } - } else { - writeByte(wr, *(*byte)(ptr)) - } - - case MYSQL_TYPE_LONGLONG, MYSQL_TYPE_DOUBLE: - writeU64(wr, *(*uint64)(ptr)) - - case MYSQL_TYPE_DATE: - writeDate(wr, *(*mysql.Date)(ptr)) - - case MYSQL_TYPE_TIMESTAMP, MYSQL_TYPE_DATETIME: - writeTime(wr, *(*time.Time)(ptr)) - - case MYSQL_TYPE_TIME: - writeDuration(wr, *(*time.Duration)(ptr)) - - default: - panic(BIND_UNK_TYPE) - } - return -} diff --git a/src/github.com/ziutek/mymysql/thrsafe/LICENSE b/src/github.com/ziutek/mymysql/thrsafe/LICENSE deleted file mode 100644 index 8eab9a6..0000000 --- a/src/github.com/ziutek/mymysql/thrsafe/LICENSE +++ /dev/null @@ -1,24 +0,0 @@ -Copyright (c) 2010, Michal Derkacz -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: -1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. -2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. -3. The name of the author may not be used to endorse or promote products - derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR -IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT -NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/src/github.com/ziutek/mymysql/thrsafe/Makefile b/src/github.com/ziutek/mymysql/thrsafe/Makefile deleted file mode 100644 index 6315ce3..0000000 --- a/src/github.com/ziutek/mymysql/thrsafe/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -include $(GOROOT)/src/Make.inc - -TARG=github.com/ziutek/mymysql/thrsafe -GOFILES=\ - thrsafe.go\ - -include $(GOROOT)/src/Make.pkg diff --git a/src/github.com/ziutek/mymysql/thrsafe/thrsafe.go b/src/github.com/ziutek/mymysql/thrsafe/thrsafe.go deleted file mode 100644 index 98437a5..0000000 --- a/src/github.com/ziutek/mymysql/thrsafe/thrsafe.go +++ /dev/null @@ -1,236 +0,0 @@ -// Thread safe engine for MyMySQL - -// See documentation of mymysql/native for details/ -package thrsafe - -import ( - "sync" - //"log" - "github.com/ziutek/mymysql/mysql" - _ "github.com/ziutek/mymysql/native" -) - -type Conn struct { - mysql.Conn - mutex *sync.Mutex -} - -func (c *Conn) lock() { - //log.Println(c, ":: lock @", c.mutex) - c.mutex.Lock() -} - -func (c *Conn) unlock() { - //log.Println(c, ":: unlock @", c.mutex) - c.mutex.Unlock() -} - -type Result struct { - mysql.Result - conn *Conn -} - -type Stmt struct { - mysql.Stmt - conn *Conn -} - -type Transaction struct { - *Conn - conn *Conn -} - -func New(proto, laddr, raddr, user, passwd string, db ...string) mysql.Conn { - return &Conn{ - Conn: orgNew(proto, laddr, raddr, user, passwd, db...), - mutex: new(sync.Mutex), - } -} - -func (c *Conn) Connect() error { - //log.Println("Connect") - c.lock() - defer c.unlock() - return c.Conn.Connect() -} - -func (c *Conn) Close() error { - //log.Println("Close") - c.lock() - defer c.unlock() - return c.Conn.Close() -} - -func (c *Conn) Reconnect() error { - //log.Println("Reconnect") - c.lock() - defer c.unlock() - return c.Conn.Reconnect() -} - -func (c *Conn) Use(dbname string) error { - //log.Println("Use") - c.lock() - defer c.unlock() - return c.Conn.Use(dbname) -} - -func (c *Conn) Start(sql string, params ...interface{}) (mysql.Result, error) { - //log.Println("Start") - c.lock() - res, err := c.Conn.Start(sql, params...) - // Unlock if error or OK result (which doesn't provide any fields) - if err != nil { - c.unlock() - return nil, err - } - if len(res.Fields()) == 0 { - c.unlock() - } - return &Result{Result: res, conn: c}, err -} - -func (res *Result) GetRow() (mysql.Row, error) { - //log.Println("GetRow") - if len(res.Result.Fields()) == 0 { - // There is no fields in result (OK result) - return nil, nil - } - row, err := res.Result.GetRow() - if err != nil || row == nil && !res.MoreResults() { - res.conn.unlock() - } - return row, err -} - -func (res *Result) NextResult() (mysql.Result, error) { - //log.Println("NextResult") - next, err := res.Result.NextResult() - if err != nil { - return nil, err - } - return &Result{next, res.conn}, nil -} - -func (c *Conn) Ping() error { - c.lock() - defer c.unlock() - return c.Conn.Ping() -} - -func (c *Conn) Prepare(sql string) (mysql.Stmt, error) { - //log.Println("Prepare") - c.lock() - defer c.unlock() - stmt, err := c.Conn.Prepare(sql) - if err != nil { - return nil, err - } - return &Stmt{Stmt: stmt, conn: c}, nil -} - -func (stmt *Stmt) Run(params ...interface{}) (mysql.Result, error) { - //log.Println("Run") - stmt.conn.lock() - res, err := stmt.Stmt.Run(params...) - // Unlock if error or OK result (which doesn't provide any fields) - if err != nil { - stmt.conn.unlock() - return nil, err - } - if len(res.Fields()) == 0 { - stmt.conn.unlock() - } - return &Result{Result: res, conn: stmt.conn}, nil -} - -func (stmt *Stmt) Delete() error { - //log.Println("Delete") - stmt.conn.lock() - defer stmt.conn.unlock() - return stmt.Stmt.Delete() -} - -func (stmt *Stmt) Reset() error { - //log.Println("Reset") - stmt.conn.lock() - defer stmt.conn.unlock() - return stmt.Stmt.Reset() -} - -func (stmt *Stmt) SendLongData(pnum int, data interface{}, pkt_size int) error { - //log.Println("SendLongData") - stmt.conn.lock() - defer stmt.conn.unlock() - return stmt.Stmt.SendLongData(pnum, data, pkt_size) -} - -func (c *Conn) Query(sql string, params ...interface{}) ([]mysql.Row, mysql.Result, error) { - return mysql.Query(c, sql, params...) -} - -func (stmt *Stmt) Exec(params ...interface{}) ([]mysql.Row, mysql.Result, error) { - return mysql.Exec(stmt, params...) -} - -func (res *Result) End() error { - return mysql.End(res) -} - -func (res *Result) GetRows() ([]mysql.Row, error) { - return mysql.GetRows(res) -} - -// Begins a new transaction. No any other thread can send command on this -// connection until Commit or Rollback will be called. -func (c *Conn) Begin() (mysql.Transaction, error) { - //log.Println("Begin") - c.lock() - tr := Transaction{ - &Conn{c.Conn, new(sync.Mutex)}, - c, - } - _, err := c.Conn.Start("START TRANSACTION") - if err != nil { - c.unlock() - return nil, err - } - return &tr, nil -} - -func (tr *Transaction) end(cr string) error { - tr.lock() - _, err := tr.conn.Conn.Start(cr) - tr.conn.unlock() - // Invalidate this transaction - m := tr.Conn.mutex - tr.Conn = nil - tr.conn = nil - m.Unlock() // One goorutine which still uses this transaction will panic - return err -} - -func (tr *Transaction) Commit() error { - //log.Println("Commit") - return tr.end("COMMIT") -} - -func (tr *Transaction) Rollback() error { - //log.Println("Rollback") - return tr.end("ROLLBACK") -} - -func (tr *Transaction) Do(st mysql.Stmt) mysql.Stmt { - if s, ok := st.(*Stmt); ok && s.conn == tr.conn { - // Returns new statement which uses statement mutexes - return &Stmt{s.Stmt, tr.Conn} - } - panic("Transaction and statement doesn't belong to the same connection") -} - -var orgNew func(proto, laddr, raddr, user, passwd string, db ...string) mysql.Conn - -func init() { - orgNew = mysql.New - mysql.New = New -} diff --git a/src/github.com/ziutek/mymysql/thrsafe/thrsafe_test.go b/src/github.com/ziutek/mymysql/thrsafe/thrsafe_test.go deleted file mode 100644 index 6b215b2..0000000 --- a/src/github.com/ziutek/mymysql/thrsafe/thrsafe_test.go +++ /dev/null @@ -1 +0,0 @@ -package thrsafe From b75071848b3030115d7a38ea72a38dec6fc4887b Mon Sep 17 00:00:00 2001 From: Julien Schmidt Date: Wed, 9 Jan 2013 00:59:26 +0100 Subject: [PATCH 2/3] Update README --- README | 9 --------- 1 file changed, 9 deletions(-) diff --git a/README b/README index 55e1ea3..90bc602 100644 --- a/README +++ b/README @@ -3,15 +3,6 @@ drivers (for the database/sql package). To run these tests, in this directory, run: -$ export GOPATH=$PWD - -... ignoring your existing GOPATH. (This project imports all the 3rd -party drivers here, to make things easier to track, and to enable -local fixes while waiting for upstream.) - -Then: - -$ cd src/sqltest $ go test -v or, most of the time, skipping the annoyingly long tests: From 397cb83fd967453e8b2a4b294d768d18df410756 Mon Sep 17 00:00:00 2001 From: Julien Schmidt Date: Wed, 20 Mar 2013 01:19:48 +0100 Subject: [PATCH 3/3] Update driver imports --- drivers.go | 4 ++-- sql_test.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers.go b/drivers.go index eb7cd6f..1ddea72 100644 --- a/drivers.go +++ b/drivers.go @@ -1,8 +1,8 @@ package sqltest import ( + _ "github.com/go-sql-driver/mysql" + _ "github.com/lib/pq" _ "github.com/mattn/go-sqlite3" _ "github.com/ziutek/mymysql/godrv" - _ "github.com/Go-SQL-Driver/MySQL" - _ "github.com/bmizerany/pq" ) diff --git a/sql_test.go b/sql_test.go index 1eb9566..ee3d638 100644 --- a/sql_test.go +++ b/sql_test.go @@ -25,7 +25,7 @@ var ( pq Tester = &pqDB{} ) -// pqDB validates the postgres driver by Blake Mizerany (github.com/bmizerany/pq.go) +// pqDB validates the postgres driver by Blake Mizerany (github.com/lib/pq.go) type pqDB struct { once sync.Once // guards init of running running bool // whether port 5432 is listening