Skip to content

Commit 9211f07

Browse files
author
Philio
committed
misc improvements
1 parent 6eef300 commit 9211f07

File tree

5 files changed

+115
-44
lines changed

5 files changed

+115
-44
lines changed

README.markdown

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ Revision History
77

88
0.3.x series [testing]
99

10-
* 0.3.0-RC - Fixed SimpleTest unit test.
10+
* 0.3.0-RC - Fixed SimpleTest unit test. Fixed and variable length strings for normal queries now return string types not []byte, text/blobs are indistinguishable so are left in []byte format. All integer values in prepared statements are stored as either int64 or uint64 depending on the unsigned flag, this simplifies conversion greatly when binding the result. Added ParamCount() and RowCount() methods to statements. The built in Date, Time and DateTime types can now be bound as strings in statements.
1111
* 0.3.0-beta-1 - Added full statement and functions. Refactored packet handlers into generic functions. Added new BindResult/Fetch method to get result data from prepared statements. Added type conversions for similar types to populate the result pointers with values from the row data. Added simple type conversion to standard queries. Added automatic reconnect for a select number of operations. Added greater number of client errors from the MySQL manual. Added date/time types to allow date/time elements to be stored as integers and ints, making them more useful.
1212
* 0.3.0-alpha-3 - Added new error structs ClientError and ServerError. Replaced majority of os.Error/os.NewError functionality with MySQL specific ClientError objects. Server error responses now return a ServerError. Removed Client.Errno and Client.Error. Added deferred error processing to reader, writer and packets to catch and errors and always return a ClientError. Rewrote auto reconnect to check for specific MySQL error codes.
1313
* 0.3.0-alpha-2 - Added transaction wrappers, Added auto-reconnect functionality to repeatable methods. Removed mutex lock/unlocking, as it is now more appropriate that the application decides when thread safe functions are required and it's considerably safer to have a sequence such as Client.Lock(), Client.Query(...), Client.Unlock(). Added a new test which performs create, drop, select, insert and update queries on a simple demo table to test the majority of the library functionality. Added additional error messages to places where an error could be returned but there was no error number/string set. Many small changes and general improvements.
@@ -236,6 +236,8 @@ Statement methods
236236

237237
**Statement.Prepare(sql string) (err os.Error)** - Prepare a new statement using the supplied query.
238238

239+
**Statement.ParamCount() uint16** - Get the number of parameters.
240+
239241
**Statement.BindParams(params ...interface{}) (err os.Error)** - Bind parameters to the statement.
240242

241243
**Statement.SendLongData(num int, data []byte) (err os.Error)** - Send a parameter as long data. The data can be > than the maximum packet size and will be split automatically.
@@ -250,6 +252,8 @@ Statement methods
250252

251253
**Statement.BindResult(params ...interface{}) (err os.Error)** - Bind the result, parameters passed to this functions should be pointers to variables which will be populated with the data from the fetched row. If a column value is not needed a nil can be used. Parameters should be of a "similar" type to the actual column value in the MySQL table, e.g. for an INT field, the parameter can be any integer type or a string and the relevant conversion is performed. Using integer sizes smaller than the size in the table is not recommended. The number of parameters bound can be equal or less than the number of fields in the table, providing more parameters than actual columns will result in a crash.
252254

255+
**Statement.RowCount() uint64** - Get the number of rows in the result set, works for stored results only, otherwise returns 0.
256+
253257
**Statement.Fetch() (eof bool, err os.Error)** - Fetch the next row in the result, values are populated into parameters bound using BindResult.
254258

255259
**Statement.StoreResult() (err os.Error)** - Store all rows for a result set,

convert.go

Lines changed: 11 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -221,24 +221,8 @@ func lcbtob(n uint64) (b []byte) {
221221
}
222222

223223
// any to uint64
224-
func atou64(i interface{}) (n uint64) {
224+
func atoui64(i interface{}) (n uint64) {
225225
switch t := i.(type) {
226-
case int:
227-
n = uint64(t)
228-
case uint:
229-
n = uint64(t)
230-
case int8:
231-
n = uint64(t)
232-
case uint8:
233-
n = uint64(t)
234-
case int16:
235-
n = uint64(t)
236-
case uint16:
237-
n = uint64(t)
238-
case int32:
239-
n = uint64(t)
240-
case uint32:
241-
n = uint64(t)
242226
case int64:
243227
n = uint64(t)
244228
case uint64:
@@ -278,14 +262,22 @@ func atof64(i interface{}) (f float64) {
278262
// any to string
279263
func atos(i interface{}) (s string) {
280264
switch t := i.(type) {
281-
case int, uint, int8, uint8, int16, uint16, int32, uint32, int64, uint64:
282-
s = strconv.Uitoa64(atou64(i))
265+
case int64:
266+
s = strconv.Itoa64(t)
267+
case uint64:
268+
s = strconv.Uitoa64(t)
283269
case float32:
284270
s = strconv.Ftoa32(t, 'f', -1)
285271
case float64:
286272
s = strconv.Ftoa64(t, 'f', -1)
287273
case []byte:
288274
s = string(t)
275+
case Date:
276+
return t.String()
277+
case Time:
278+
return t.String()
279+
case DateTime:
280+
return t.String()
289281
case string:
290282
return t
291283
default:

handler.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ func handleRow(p *packetRowData, c *Client, r *Result) (err os.Error) {
145145
return
146146
}
147147
// Strings
148-
case FIELD_TYPE_DECIMAL, FIELD_TYPE_NEWDECIMAL, FIELD_TYPE_VARCHAR:
148+
case FIELD_TYPE_DECIMAL, FIELD_TYPE_NEWDECIMAL, FIELD_TYPE_VARCHAR, FIELD_TYPE_VAR_STRING, FIELD_TYPE_STRING:
149149
field = string(p.row[i].([]byte))
150150
// Anything else
151151
default:
@@ -230,25 +230,25 @@ func handleBinaryRow(p *packetRowBinary, c *Client, r *Result) (err os.Error) {
230230
// Tiny int (8 bit int unsigned or signed)
231231
case FIELD_TYPE_TINY:
232232
if f.Flags&FLAG_UNSIGNED > 0 {
233-
field = p.data[pos]
233+
field = uint64(p.data[pos])
234234
} else {
235-
field = int8(p.data[pos])
235+
field = int64(p.data[pos])
236236
}
237237
pos++
238238
// Small int (16 bit int unsigned or signed)
239239
case FIELD_TYPE_SHORT, FIELD_TYPE_YEAR:
240240
if f.Flags&FLAG_UNSIGNED > 0 {
241-
field = btoui16(p.data[pos : pos+2])
241+
field = uint64(btoui16(p.data[pos : pos+2]))
242242
} else {
243-
field = btoi16(p.data[pos : pos+2])
243+
field = int64(btoi16(p.data[pos : pos+2]))
244244
}
245245
pos += 2
246246
// Int (32 bit int unsigned or signed) and medium int which is actually in int32 format
247247
case FIELD_TYPE_LONG, FIELD_TYPE_INT24:
248248
if f.Flags&FLAG_UNSIGNED > 0 {
249-
field = btoui32(p.data[pos : pos+4])
249+
field = uint64(btoui32(p.data[pos : pos+4]))
250250
} else {
251-
field = btoi32(p.data[pos : pos+4])
251+
field = int64(btoi32(p.data[pos : pos+4]))
252252
}
253253
pos += 4
254254
// Big int (64 bit int unsigned or signed)

mysql_test.go

Lines changed: 68 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,13 @@ const (
3535
TEST_DBNAMEBAD = "gomysql_bad" // This is a nonexistant database
3636

3737
// Simple table queries
38-
CREATE_SIMPLE = "CREATE TABLE `simple` (`id` SERIAL NOT NULL, `number` BIGINT NOT NULL, `string` VARCHAR(32) NOT NULL, `text` TEXT NOT NULL, `datetime` DATETIME NOT NULL) ENGINE = InnoDB CHARACTER SET utf8 COLLATE utf8_unicode_ci COMMENT = 'GoMySQL Test Suite Simple Table';"
39-
SELECT_SIMPLE = "SELECT * FROM simple"
40-
INSERT_SIMPLE = "INSERT INTO simple VALUES (null, %d, '%s', '%s', NOW())"
41-
UPDATE_SIMPLE = "UPDATE simple SET `text` = '%s', `datetime` = NOW() WHERE id = %d"
42-
DROP_SIMPLE = "DROP TABLE `simple`"
38+
CREATE_SIMPLE = "CREATE TABLE `simple` (`id` SERIAL NOT NULL, `number` BIGINT NOT NULL, `string` VARCHAR(32) NOT NULL, `text` TEXT NOT NULL, `datetime` DATETIME NOT NULL) ENGINE = InnoDB CHARACTER SET utf8 COLLATE utf8_unicode_ci COMMENT = 'GoMySQL Test Suite Simple Table';"
39+
SELECT_SIMPLE = "SELECT * FROM simple"
40+
INSERT_SIMPLE = "INSERT INTO simple VALUES (null, %d, '%s', '%s', NOW())"
41+
INSERT_SIMPLE_STMT = "INSERT INTO simple VALUES (null, ?, ?, ?, NOW())"
42+
UPDATE_SIMPLE = "UPDATE simple SET `text` = '%s', `datetime` = NOW() WHERE id = %d"
43+
UPDATE_SIMPLE_STMT = "UPDATE simple SET `text` = ?, `datetime` = NOW() WHERE id = ?"
44+
DROP_SIMPLE = "DROP TABLE `simple`"
4345

4446
// All types table queries
4547
CREATE_ALLTYPES = "CREATE TABLE `all_types` (`id` SERIAL NOT NULL, `tiny_int` TINYINT NOT NULL, `tiny_uint` TINYINT UNSIGNED NOT NULL, `small_int` SMALLINT NOT NULL, `small_uint` SMALLINT UNSIGNED NOT NULL, `medium_int` MEDIUMINT NOT NULL, `medium_uint` MEDIUMINT UNSIGNED NOT NULL, `int` INT NOT NULL, `uint` INT UNSIGNED NOT NULL, `big_int` BIGINT NOT NULL, `big_uint` BIGINT UNSIGNED NOT NULL, `decimal` DECIMAL(10,4) NOT NULL, `float` FLOAT NOT NULL, `double` DOUBLE NOT NULL, `real` REAL NOT NULL, `bit` BIT(32) NOT NULL, `boolean` BOOLEAN NOT NULL, `date` DATE NOT NULL, `datetime` DATETIME NOT NULL, `timestamp` TIMESTAMP NOT NULL, `time` TIME NOT NULL, `year` YEAR NOT NULL, `char` CHAR(32) NOT NULL, `varchar` VARCHAR(32) NOT NULL, `tiny_text` TINYTEXT NOT NULL, `text` TEXT NOT NULL, `medium_text` MEDIUMTEXT NOT NULL, `long_text` LONGTEXT NOT NULL, `binary` BINARY(32) NOT NULL, `var_binary` VARBINARY(32) NOT NULL, `tiny_blob` TINYBLOB NOT NULL, `medium_blob` MEDIUMBLOB NOT NULL, `blob` BLOB NOT NULL, `long_blob` LONGBLOB NOT NULL, `enum` ENUM('a','b','c','d','e') NOT NULL, `set` SET('a','b','c','d','e') NOT NULL, `geometry` GEOMETRY NOT NULL) ENGINE = InnoDB CHARACTER SET utf8 COLLATE utf8_unicode_ci COMMENT = 'GoMySQL Test Suite All Types Table'"
@@ -51,6 +53,14 @@ var (
5153
err os.Error
5254
)
5355

56+
type SimpleRow struct {
57+
Id uint64
58+
Number int64
59+
String string
60+
Text string
61+
Date Date
62+
}
63+
5464
// Test connect to server via TCP
5565
func TestDialTCP(t *testing.T) {
5666
t.Logf("Running DialTCP test to %s:%s", TEST_HOST, TEST_PORT)
@@ -171,7 +181,7 @@ func TestSimple(t *testing.T) {
171181
break
172182
}
173183
id := row[0].(uint64)
174-
num, str1, str2 := strconv.Itoa64(row[1].(int64)), string(row[2].([]byte)), string(row[3].([]byte))
184+
num, str1, str2 := strconv.Itoa64(row[1].(int64)), row[2].(string), string(row[3].([]byte))
175185
if rowMap[id][0] != num || rowMap[id][1] != str1 || rowMap[id][2] != str2 {
176186
t.Logf("String from database doesn't match local string")
177187
t.Fail()
@@ -215,7 +225,7 @@ func TestSimple(t *testing.T) {
215225
break
216226
}
217227
id := row[0].(uint64)
218-
num, str1, str2 := strconv.Itoa64(row[1].(int64)), string(row[2].([]byte)), string(row[3].([]byte))
228+
num, str1, str2 := strconv.Itoa64(row[1].(int64)), row[2].(string), string(row[3].([]byte))
219229
if rowMap[id][0] != num || rowMap[id][1] != str1 || rowMap[id][2] != str2 {
220230
t.Logf("%#v %#v", rowMap[id], row)
221231
t.Logf("String from database doesn't match local string")
@@ -242,6 +252,57 @@ func TestSimple(t *testing.T) {
242252
}
243253
}
244254

255+
// Test queries on a simple table (create database, select, insert, update, drop database) using a statement
256+
func TestSimpleStatement(t *testing.T) {
257+
t.Logf("Running simple table statement tests")
258+
db, err = DialUnix(TEST_SOCK, TEST_USER, TEST_PASSWD, TEST_DBNAME)
259+
if err != nil {
260+
t.Logf("Error %s", err)
261+
t.Fail()
262+
}
263+
t.Logf("Init statement")
264+
stmt, err := db.InitStmt()
265+
if err != nil {
266+
t.Logf("Error %s", err)
267+
t.Fail()
268+
}
269+
t.Logf("Prepare create table")
270+
err = stmt.Prepare(CREATE_SIMPLE)
271+
if err != nil {
272+
t.Logf("Error %s", err)
273+
t.Fail()
274+
}
275+
t.Logf("Execute create table")
276+
err = stmt.Execute()
277+
if err != nil {
278+
t.Logf("Error %s", err)
279+
t.Fail()
280+
}
281+
t.Logf("Prepare insert")
282+
err = stmt.Prepare(INSERT_SIMPLE_STMT)
283+
if err != nil {
284+
t.Logf("Error %s", err)
285+
t.Fail()
286+
}
287+
t.Logf("Insert 1000 records")
288+
rowMap := make(map[uint64][]string)
289+
for i := 0; i < 1000; i++ {
290+
num, str1, str2 := rand.Int(), randString(32), randString(128)
291+
err = stmt.BindParams(num, str1, str2)
292+
if err != nil {
293+
t.Logf("Error %s", err)
294+
t.Fail()
295+
}
296+
err = stmt.Execute()
297+
if err != nil {
298+
t.Logf("Error %s", err)
299+
t.Fail()
300+
}
301+
row := []string{fmt.Sprintf("%d", num), str1, str2}
302+
rowMap[db.LastInsertId] = row
303+
}
304+
}
305+
245306
// Benchmark connect/handshake via TCP
246307
func BenchmarkDialTCP(b *testing.B) {
247308
for i := 0; i < b.N; i++ {

statement.go

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,11 @@ func (s *Statement) Prepare(sql string) (err os.Error) {
8686
return
8787
}
8888

89+
// Get number of params
90+
func (s *Statement) ParamCount() uint16 {
91+
return s.paramCount
92+
}
93+
8994
// Bind params
9095
func (s *Statement) BindParams(params ...interface{}) (err os.Error) {
9196
// Check prepared
@@ -331,6 +336,15 @@ func (s *Statement) BindResult(params ...interface{}) (err os.Error) {
331336
return
332337
}
333338

339+
// Get row count
340+
func (s *Statement) RowCount() uint64 {
341+
// Stored mode
342+
if s.checkResult() && s.result.mode == RESULT_STORED {
343+
return uint64(len(s.result.rows))
344+
}
345+
return 0
346+
}
347+
334348
// Fetch next row
335349
func (s *Statement) Fetch() (eof bool, err os.Error) {
336350
// Log fetch
@@ -381,25 +395,25 @@ func (s *Statement) Fetch() (eof bool, err os.Error) {
381395
switch t := v.(type) {
382396
// Integer types
383397
case *int:
384-
*t = int(atou64(row[k]))
398+
*t = int(atoui64(row[k]))
385399
case *uint:
386-
*t = uint(atou64(row[k]))
400+
*t = uint(atoui64(row[k]))
387401
case *int8:
388-
*t = int8(atou64(row[k]))
402+
*t = int8(atoui64(row[k]))
389403
case *uint8:
390-
*t = uint8(atou64(row[k]))
404+
*t = uint8(atoui64(row[k]))
391405
case *int16:
392-
*t = int16(atou64(row[k]))
406+
*t = int16(atoui64(row[k]))
393407
case *uint16:
394-
*t = uint16(atou64(row[k]))
408+
*t = uint16(atoui64(row[k]))
395409
case *int32:
396-
*t = int32(atou64(row[k]))
410+
*t = int32(atoui64(row[k]))
397411
case *uint32:
398-
*t = uint32(atou64(row[k]))
412+
*t = uint32(atoui64(row[k]))
399413
case *int64:
400-
*t = int64(atou64(row[k]))
414+
*t = int64(atoui64(row[k]))
401415
case *uint64:
402-
*t = atou64(row[k])
416+
*t = atoui64(row[k])
403417
// Floating point types
404418
case *float32:
405419
*t = float32(atof64(row[k]))

0 commit comments

Comments
 (0)