Browse Source

Milestone 1

Julien Schmidt 13 years ago
commit
b8ae1f22a6
10 changed files with 2470 additions and 0 deletions
  1. 373 0
      LICENSE
  2. 285 0
      connection.go
  3. 129 0
      const.go
  4. 75 0
      driver.go
  5. 931 0
      packets.go
  6. 22 0
      result.go
  7. 65 0
      rows.go
  8. 272 0
      statement.go
  9. 27 0
      transaction.go
  10. 291 0
      utils.go

+ 373 - 0
LICENSE

@@ -0,0 +1,373 @@
+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.

+ 285 - 0
connection.go

@@ -0,0 +1,285 @@
+// 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"
+	"net"
+	"strconv"
+	"time"
+)
+
+type mysqlConn struct {
+	cfg            *config
+	server         *serverSettings
+	netConn        net.Conn
+	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":
+			debug("TLS-Encryption not implemented yet")
+
+		// Compression
+		case "compress":
+			debug("TLS-Encryption not implemented yet")
+
+		// We don't want to set keepalive as system var
+		case "keepalive":
+			break
+
+		// 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.netConn = nil
+	return
+}
+
+func (mc *mysqlConn) Prepare(query string) (ds driver.Stmt, e error) {
+	// Send command
+	e = mc.writeCommandPacket(COM_STMT_PREPARE, query)
+	if e != nil {
+		return
+	}
+
+	stmt := mysqlStmt{new(stmtContent)}
+	stmt.mc = mc
+
+	// Read Result
+	var columnCount, paramCount uint16
+	stmt.id, columnCount, paramCount, e = mc.readPrepareResultPacket()
+	if e != nil {
+		return
+	}
+
+	if paramCount > 0 {
+		stmt.params, e = stmt.mc.readColumns(int(paramCount))
+		if e != nil {
+			return
+		}
+	}
+	stmt.paramCount = int(paramCount)
+
+	if columnCount > 0 {
+		_, e = stmt.mc.readColumns(int(columnCount))
+		if e != nil {
+			return
+		}
+	}
+
+	stmt.query = query
+	ds = stmt
+	return
+}
+
+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
+}

+ 129 - 0
const.go

@@ -0,0 +1,129 @@
+// 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.000000000"
+)
+
+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
+)

+ 75 - 0
driver.go

@@ -0,0 +1,75 @@
+// 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"
+	"database/sql/driver"
+	"errors"
+	"fmt"
+	"net"
+)
+
+func debug(msg string) {
+	fmt.Println("DEBUG MySQL: " + msg)
+}
+
+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
+	}
+
+	// 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{})
+}

+ 931 - 0
packets.go

@@ -0,0 +1,931 @@
+// 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"
+	"io"
+	"time"
+)
+
+// Read packet to buffer 'data'
+func (mc *mysqlConn) readPacket() (data []byte, e error) {
+	// Packet Length
+	pktLen, e := mc.readNumber(3)
+	if e != nil {
+		return
+	}
+
+	if int(pktLen) == 0 {
+		return
+	}
+
+	// Packet Number
+	pktSeq, e := mc.readNumber(1)
+	if e != nil {
+		return
+	}
+
+	// Check Packet Sync
+	if uint8(pktSeq) != mc.sequence {
+		e = errors.New("Commands out of sync; you can't run this command now")
+		return
+	}
+	mc.sequence++
+
+	// Read rest of packet
+	data = make([]byte, pktLen)
+	n, e := mc.netConn.Read(data)
+	if e != nil || n != int(pktLen) {
+		fmt.Println(e)
+		e = driver.ErrBadConn
+		return
+	}
+	return
+}
+
+// Send Packet with given data
+func (mc *mysqlConn) writePacket(data []byte) (e error) {
+	// Set time BEFORE to avoid possible collisions
+	if mc.server.keepalive > 0 {
+		mc.lastCmdTime = time.Now()
+	}
+
+	pktLen := uint32(len(data))
+	if int(pktLen) == 0 {
+		debug("WRITE 0 Length; ABORTING!")
+		return
+	}
+
+	// Add the packet header
+	pktData := make([]byte, 0, len(data)+4)
+	pktData = append(pktData, uint24ToBytes(pktLen)...)
+	pktData = append(pktData, mc.sequence)
+	pktData = append(pktData, data...)
+
+	// Write packet
+	n, e := mc.netConn.Write(pktData)
+	if e != nil || n != len(pktData) {
+		fmt.Println("BadConn:", e)
+		e = driver.ErrBadConn
+		return
+	}
+
+	mc.sequence++
+	return
+}
+
+// Read n bytes long number num
+func (mc *mysqlConn) readNumber(n uint8) (num uint64, e error) {
+	// Read bytes into array
+	buf := make([]byte, n)
+
+	nr, err := io.ReadFull(mc.netConn, buf)
+	if err != nil || nr != int(n) {
+		fmt.Println(e)
+		e = driver.ErrBadConn
+		return
+	}
+
+	// Convert to uint64
+	num = 0
+	for i := uint8(0); i < n; i++ {
+		num |= uint64(buf[i]) << (i * 8)
+	}
+	return
+}
+
+/* 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) {
+	debug("Reading handshake initialization packet from server")
+
+	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 = errors.New(fmt.Sprintf("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) {
+	debug("Sending authentication packet to server")
+
+	// 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
+	dataLen := 4 + 4 + 1 + 23 + len(mc.cfg.user) + 1 + 1 + len(scrambleBuff) + len(mc.cfg.dbname) + 1
+	data := make([]byte, 0, dataLen)
+
+	// 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
+	mc.writePacket(data)
+	return
+}
+
+// Returns error if Packet is not an 'Result OK'-Packet
+func (mc *mysqlConn) readResultOK() (e error) {
+	debug("Read result from server")
+	data, e := mc.readPacket()
+	if e != nil {
+		return
+	}
+
+	switch data[0] {
+	case 0:
+		debug("OK Packet")
+		return mc.handleOkPacket(data)
+	case 255:
+		debug("Error Packet")
+		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 a 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
+	}
+	//pos += n
+
+	// Server status [16 bit uint]
+	//serverStatus := bytesToUint16(data[pos : pos+2])
+	//pos += 2
+
+	// Warning [16 bit uint]
+	//warningCount := bytesToUint16(data[pos : pos+2])
+	//pos += 2
+
+	//var message string
+	// Message (optional) [string]
+	//if pos < len(data) {
+	//	message = string(data[pos:])
+	//}
+
+	mc.affectedRows = affectedRows
+	mc.insertId = insertID
+	
+	return
+}
+
+/* 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 (mc *mysqlConn) readPrepareResultPacket() (stmtID uint32, columnCount uint16, paramCount uint16, e error) {
+	debug("Read result from server")
+	data, e := mc.readPacket()
+	if e != nil {
+		return
+	}
+
+	// Position
+	pos := 0
+
+	if data[pos] != 0 {
+		e = mc.handleErrorPacket(data)
+		return
+	}
+	pos++
+
+	stmtID = 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]
+	paramCount = bytesToUint16(data[pos : pos+2])
+	pos += 2
+
+	// Warning count [16 bit uint]
+	// bytesToUint16(data[pos : pos+2])
+
+	return
+}
+
+/* Command Packet
+Bytes                        Name
+-----                        ----
+1                            command
+n                            arg
+*/
+func (mc *mysqlConn) writeCommandPacket(command commandType, args ...interface{}) (e error) {
+	// Reset Packet Sequence
+	mc.sequence = 0
+
+	// Make slice from command byte
+	data := []byte{byte(command)}
+
+	switch command {
+
+	// Commands without args
+	case COM_QUIT, COM_PING:
+		if len(args) > 0 {
+			return errors.New(fmt.Sprintf("Too much arguments (Got: %d Has:0)", len(args)))
+		}
+
+	// Commands with 1 arg unterminated string
+	case COM_QUERY, COM_STMT_PREPARE:
+		if len(args) != 1 {
+			return errors.New(fmt.Sprintf("Invalid arguments count (Got:%d Need: 1)", len(args)))
+		}
+		data = append(data, []byte(args[0].(string))...)
+
+	// Commands with 1 arg 32 bit uint
+	case COM_STMT_CLOSE:
+		if len(args) != 1 {
+			return errors.New(fmt.Sprintf("Invalid arguments count (Got:%d Need: 1)", len(args)))
+		}
+		data = append(data, uint32ToBytes(args[0].(uint32))...)
+	default:
+		return errors.New(fmt.Sprintf("Unknown command: %d", command))
+	}
+
+	// Send CMD packet
+	return mc.writePacket(data)
+}
+
+/* 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) {
+	debug("Read Result Set Header Packet from server")
+	data, e := mc.readPacket()
+	if e != nil {
+		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
+}
+
+/* Parameter Packet
+Bytes                   Name
+ -----                   ----
+ 2                       type
+ 2                       flags
+ 1                       decimals
+ 4                       length
+*/
+// Read Packets as Field Packets until EOF-Packet or an Error appears
+/*func (mc *mysqlConn) readParams(n int) (params []*mysqlField, e error) {
+	debug("Reading Params")
+
+	var data []byte
+
+	for {
+		data, e = mc.readPacket()
+		if e != nil {
+			return
+		}
+
+		// EOF Packet
+		if data[0] == 254 && len(data) == 5 {
+			if len(params) != n {
+				e = errors.New(fmt.Sprintf("ParamsCount mismatch n:%d len:%d", n, len(params)))
+			}
+			return
+		}
+
+		var pos int
+
+		// Field type [byte]
+		fieldType := data[pos : pos+2]
+		pos += 2
+
+		// Flags [16 bit uint]
+		flags := bytesToUint16(data[pos : pos+2])
+		pos += 2
+
+		// Decimals [8 bit uint]
+		decimals := data[pos]
+		pos++
+
+		// Length [32 bit uint]
+		length := bytesToUint32(data[pos : pos+4])
+		pos += 4
+
+		fmt.Printf("length=%d fieldType=%d flags=%d decimals=%d \n", length, fieldType, flags, decimals)
+
+		params = append(params, &mysqlField{})
+		//params = append(params, &mysqlField{name: name, fieldType: fieldType1})
+	}
+
+	return
+}*/
+
+// Read Packets as Field Packets until EOF-Packet or an Error appears
+func (mc *mysqlConn) readColumns(n int) (columns []*mysqlField, e error) {
+	debug("Reading Columns")
+
+	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 = errors.New(fmt.Sprintf("ColumnsCount mismatch n:%d len:%d", n, len(columns)))
+			}
+			return
+		}
+
+		var pos, n int
+		var catalog, database, table, orgTable, name, orgName []byte
+		var defaultVal uint64
+
+		// Catalog
+		catalog, n, _, e = readLengthCodedBinary(data)
+		if e != nil {
+			return
+		}
+		pos += n
+
+		// Database [len coded string]
+		database, n, _, e = readLengthCodedBinary(data[pos:])
+		if e != nil {
+			return
+		}
+		pos += n
+
+		// Table [len coded string]
+		table, n, _, e = readLengthCodedBinary(data[pos:])
+		if e != nil {
+			return
+		}
+		pos += n
+
+		// Original table [len coded string]
+		orgTable, n, _, e = readLengthCodedBinary(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:])
+		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:])
+		}
+
+		fmt.Printf("catalog=%s database=%s table=%s orgTable=%s name=%s orgName=%s charsetNumber=%d length=%d fieldType=%d flags=%d decimals=%d defaultVal=%d \n", catalog, database, table, orgTable, name, orgName, charsetNumber, length, fieldType, flags, decimals, defaultVal)
+
+		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) {
+	debug("Reading Rows")
+
+	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, 0, columnsCount)
+		pos = 0
+
+		for i = 0; i < columnsCount; i++ {
+			// Read bytes and convert to string
+			var value []byte
+			value, n, isNull, e = readLengthCodedBinary(data[pos:])
+			if e != nil {
+				return
+			}
+
+			// Append nil if field is NULL
+			if isNull {
+				row = append(row, nil)
+			} else {
+				row = append(row, &value)
+			}
+			pos += n
+		}
+		rows = append(rows, &row)
+	}
+
+	mc.affectedRows = uint64(len(rows))
+	return
+}
+
+func (mc *mysqlConn) readBinaryRows(rc *rowsContent) (e error) {
+	debug("Reading Rows")
+
+	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++
+				fmt.Println("TINY", string(*row[i]))
+
+			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
+				fmt.Println("SHORT", string(*row[i]))
+
+			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
+				fmt.Println("LONG", string(*row[i]))
+
+			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
+				fmt.Println("LONGLONG", string(*row[i]))
+
+			case FIELD_TYPE_FLOAT:
+				row[i] = float32ToByteStr(bytesToFloat32(data[pos : pos+4]))
+				pos += 4
+				fmt.Println("FLOAT", string(*row[i]))
+
+			case FIELD_TYPE_DOUBLE:
+				row[i] = float64ToByteStr(bytesToFloat64(data[pos : pos+8]))
+				pos += 8
+				fmt.Println("DOUBLE", string(*row[i]))
+
+			case FIELD_TYPE_DECIMAL, FIELD_TYPE_NEWDECIMAL:
+				var tmp []byte
+				tmp, n, isNull, e = readLengthCodedBinary(data[pos:])
+				if e != nil {
+					return
+				}
+
+				if isNull && rc.columns[i].flags&FLAG_NOT_NULL == 0 {
+					row[i] = nil
+					fmt.Println("DECIMAL", nil)
+				} else {
+					row[i] = &tmp
+					fmt.Println("DECIMAL", string(tmp))
+				}
+				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:
+				var tmp []byte
+				tmp, n, isNull, e = readLengthCodedBinary(data[pos:])
+				if e != nil {
+					return
+				}
+
+				if isNull && rc.columns[i].flags&FLAG_NOT_NULL == 0 {
+					row[i] = nil
+					fmt.Println("STRING", nil)
+				} else {
+					row[i] = &tmp
+					fmt.Println("STRING", string(tmp))
+				}
+				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
+
+				var tmp []byte
+				if num == 0 {
+					tmp = []byte("0000-00-00")
+				} else {
+					tmp = []byte(fmt.Sprintf("%04d-%02d-%02d",
+						bytesToUint16(data[pos:pos+2]),
+						data[pos+2],
+						data[pos+3]))
+				}
+				row[i] = &tmp
+				pos += int(num)
+				fmt.Println("DATE", string(*row[i]))
+
+			// Time HH:MM:SS
+			case FIELD_TYPE_TIME:
+				var num uint64
+				num, n, e = bytesToLengthCodedBinary(data[pos:])
+				if e != nil {
+					return
+				}
+				
+				var tmp []byte
+				if num == 0 {
+					tmp = []byte("00:00:00")
+				} else {
+					tmp = []byte(fmt.Sprintf("%02d:%02d:%02d",
+						data[pos+6],
+						data[pos+7],
+						data[pos+8]))
+				}
+				row[i] = &tmp
+				pos += n + int(num)
+				fmt.Println("TIME", string(*row[i]))
+
+			// 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
+
+				var tmp []byte
+				if num == 0 {
+					tmp = []byte("0000-00-00 00:00:00")
+				} else {
+					tmp = []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]))
+				}
+				row[i] = &tmp
+				pos += int(num)
+				fmt.Println("DATE", string(*row[i]))
+			
+			// 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
+}
+
+// Reads Packets Packets until EOF-Packet or an Error appears. Returns count of Packets read
+func (mc *mysqlConn) readUntilEOF() (count uint64, e error) {
+	debug("Reading Until EOF")
+	var data []byte
+
+	for {
+		data, e = mc.readPacket()
+		if e != nil {
+			return
+		}
+
+		// EOF Packet
+		if data[0] == 254 && len(data) == 5 {
+			return
+		}
+
+		count++
+	}
+	return
+}

+ 22 - 0
result.go

@@ -0,0 +1,22 @@
+// 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
+}

+ 65 - 0
rows.go

@@ -0,0 +1,65 @@
+// 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 for every 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 {
+		var value *[]byte
+		for i := 0; i < cap(dest); i++ {
+			value = (*rows.content.rows[0])[i]
+			
+			if value == nil {
+				dest[i] = nil
+			} else {
+				dest[i] = *value
+			}
+		}
+		rows.content.rows = rows.content.rows[1:]
+	} else {
+		return io.EOF
+	}
+	return nil
+}

+ 272 - 0
statement.go

@@ -0,0 +1,272 @@
+// 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"
+	"fmt"
+	"reflect"
+	"time"
+)
+
+type stmtContent struct {
+	mc             *mysqlConn
+	id             uint32
+	query          string
+	paramCount     int
+	params         []*mysqlField
+	args           *[]driver.Value
+	newParamsBound bool
+}
+
+type mysqlStmt struct {
+	*stmtContent
+}
+
+func (stmt mysqlStmt) Close() error {
+	e := stmt.mc.writeCommandPacket(COM_STMT_CLOSE, stmt.id)
+	stmt.params = nil
+	stmt.mc = nil
+	return e
+}
+
+func (stmt mysqlStmt) NumInput() int {
+	return stmt.paramCount
+}
+
+func (stmt mysqlStmt) Exec(args []driver.Value) (driver.Result, error) {
+	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 {
+		_, e = stmt.mc.readUntilEOF()
+		if e != nil {
+			return nil, e
+		}
+
+		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) {
+	// Send command
+	e = stmt.buildExecutePacket(&args)
+	if e != nil {
+		return nil, e
+	}
+
+	// Get Result
+	var resLen int
+	rows := new(mysqlRows)
+	rows.content = 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
+}
+
+/* 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) {
+	if len(*args) < stmt.paramCount {
+		return fmt.Errorf(
+			"Not enough Arguments to call STMT_EXEC (Got: %d Has: %d",
+			len(*args),
+			stmt.paramCount)
+	}
+	
+	// Reset packet-sequence
+	stmt.mc.sequence = 0
+
+	data := make([]byte, 0, 10)
+
+	// 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)...)
+
+	if stmt.paramCount > 0 {
+		var i int
+		
+		// build nullBitMap
+		nullBitMap := make([]byte, (stmt.paramCount+7)/8)
+		bitMask := uint64(0)
+
+		// Check for NULL fields
+		for i = 0; i < stmt.paramCount; i++ {
+			if (*args)[i] == nil {
+				fmt.Println("nil", i, (*args)[i])
+				bitMask += 1 << uint(i)
+			}
+		}
+		// Convert bitMask to bytes
+		for i = 0; i < len(nullBitMap); i++ {
+			nullBitMap[i] = byte(bitMask >> uint(i*8))
+		}
+
+		// append nullBitMap [(param_count+7)/8 bytes]
+		data = append(data, nullBitMap...)
+
+		// Check for changed Params
+		newParamsBound := true
+		if stmt.args != nil {
+			for i := 0; i < len(*args); i++ {
+				if (*args)[i] != (*stmt.args)[i] {
+					fmt.Println((*args)[i], "!=", (*stmt.args)[i])
+					newParamsBound = false
+					break
+				}
+			}
+		}
+
+		// No (new) Parameters bound or rebound
+		if !newParamsBound {
+			//newParameterBoundFlag 0 [1 byte]
+			data = append(data, byte(0))
+		} else {
+			// newParameterBoundFlag 1 [1 byte]
+			data = append(data, byte(1))
+
+			// append types and cache values
+			paramValues := make([]byte, 0)
+			var pv reflect.Value
+			for i = 0; i < stmt.paramCount; i++ {
+				switch (*args)[i].(type) {
+				case nil:
+					data = append(data, []byte{
+						byte(FIELD_TYPE_NULL),
+						0x0}...)
+					continue
+				case []byte:
+					fmt.Println("[]byte", (*args)[i])
+				case time.Time:
+					fmt.Println("time.Time", (*args)[i])
+				}
+
+				pv = reflect.ValueOf((*args)[i])
+				switch pv.Kind() {
+				case reflect.Int64:
+					data = append(data, []byte{
+					byte(FIELD_TYPE_LONGLONG),
+					0x0}...)
+					paramValues = append(paramValues, int64ToBytes(pv.Int())...)
+					fmt.Println("int64", (*args)[i])
+
+				case reflect.Float64:
+					fmt.Println("float64", (*args)[i])
+
+				case reflect.Bool:
+					data = append(data, []byte{
+					byte(FIELD_TYPE_TINY),
+					0x0}...)
+					val := pv.Bool()
+					if val {
+						paramValues = append(paramValues, byte(1))
+					} else {
+						paramValues = append(paramValues, byte(0))
+					}
+					fmt.Println("bool", (*args)[i])
+
+				case reflect.String:
+					data = append(data, []byte{
+					byte(FIELD_TYPE_STRING),
+					0x0}...)
+					val := pv.String()
+					paramValues = append(paramValues, lengthCodedBinaryToBytes(uint64(len(val)))...)
+					paramValues = append(paramValues, []byte(val)...)
+					fmt.Println("string", string([]byte(val)))
+
+				default:
+					return fmt.Errorf("Invalid Value: %s", pv.Kind().String())
+				}
+			}
+			
+			// append cached values
+			data = append(data, paramValues...)
+			fmt.Println("data", string(data))
+		}
+
+		// Save args
+		stmt.args = args
+	}
+	return stmt.mc.writePacket(data)
+}
+
+// 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
+}

+ 27 - 0
transaction.go

@@ -0,0 +1,27 @@
+// 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) {
+	debug("Tx Commit")
+	e = tx.mc.exec("COMMIT")
+	tx.mc = nil
+	return
+}
+
+func (tx *mysqlTx) Rollback() (e error) {
+	debug("Tx Rollback")
+	e = tx.mc.exec("ROLLBACK")
+	tx.mc = nil
+	return
+}

+ 291 - 0
utils.go

@@ -0,0 +1,291 @@
+// 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"
+	"math"
+	"regexp"
+	"strings"
+	"strconv"
+)
+
+var dsnPattern *regexp.Regexp
+
+func init() {
+	dsnPattern = regexp.MustCompile(
+		`^(?:(?P<user>.*?)(?::(?P<passwd>.*))?@)?` + // [user[:password]@]
+			`(?:(?P<net>[^\(]*)(?:\((?P<addr>[^\)]*)\))?)?` + // [net[(addr)]]
+			`\/(?P<dbname>.*?)` + // /dbname
+			`(?:\?(?P<params>[^\?]*))?$`) // [?param1=value1&paramN=valueN]
+}
+
+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
+}
+
+/******************************************************************************
+*                       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 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) (d *[]byte) {
+	tmp := make([]byte, 0)
+	tmp = strconv.AppendInt(tmp, i, 10)
+	return &tmp
+}
+
+func uintToByteStr(u uint64) (d *[]byte) {
+	tmp := make([]byte, 0)
+	tmp = strconv.AppendUint(tmp, u, 10)
+	return &tmp
+}
+
+func float32ToByteStr(f float32) (d *[]byte) {
+	tmp := make([]byte, 0)
+	tmp = strconv.AppendFloat(tmp, float64(f), 'f', -1, 32)
+	return &tmp
+}
+
+func float64ToByteStr(f float64) (d *[]byte) {
+	tmp := make([]byte, 0)
+	tmp = strconv.AppendFloat(tmp, f, 'f', -1, 64)
+	return &tmp
+}
+