5
5
"encoding/json"
6
6
"fmt"
7
7
"math"
8
+ "runtime"
8
9
9
10
. "github.com/go-mysql-org/go-mysql/mysql"
10
11
"github.com/go-mysql-org/go-mysql/utils"
@@ -56,18 +57,34 @@ func (s *Stmt) Close() error {
56
57
return nil
57
58
}
58
59
60
+ // https://dev.mysql.com/doc/dev/mysql-server/latest/page_protocol_com_stmt_execute.html
59
61
func (s * Stmt ) write (args ... interface {}) error {
62
+ defer clear (s .conn .queryAttributes )
60
63
paramsNum := s .params
61
64
62
65
if len (args ) != paramsNum {
63
66
return fmt .Errorf ("argument mismatch, need %d but got %d" , s .params , len (args ))
64
67
}
65
68
66
- paramTypes := make ([]byte , paramsNum << 1 )
67
- paramValues := make ([][]byte , paramsNum )
69
+ if (s .conn .capability & CLIENT_QUERY_ATTRIBUTES > 0 ) && (s .conn .includeLine >= 0 ) {
70
+ _ , file , line , ok := runtime .Caller (s .conn .includeLine )
71
+ if ok {
72
+ lineAttr := QueryAttribute {
73
+ Name : "_line" ,
74
+ Value : fmt .Sprintf ("%s:%d" , file , line ),
75
+ }
76
+ s .conn .queryAttributes = append (s .conn .queryAttributes , lineAttr )
77
+ }
78
+ }
79
+
80
+ qaLen := len (s .conn .queryAttributes )
81
+ paramTypes := make ([][]byte , paramsNum + qaLen )
82
+ paramFlags := make ([][]byte , paramsNum + qaLen )
83
+ paramValues := make ([][]byte , paramsNum + qaLen )
84
+ paramNames := make ([][]byte , paramsNum + qaLen )
68
85
69
86
//NULL-bitmap, length: (num-params+7)
70
- nullBitmap := make ([]byte , (paramsNum + 7 )>> 3 )
87
+ nullBitmap := make ([]byte , (paramsNum + qaLen + 7 )>> 3 )
71
88
72
89
length := 1 + 4 + 1 + 4 + ((paramsNum + 7 ) >> 3 ) + 1 + (paramsNum << 1 )
73
90
@@ -76,76 +93,89 @@ func (s *Stmt) write(args ...interface{}) error {
76
93
for i := range args {
77
94
if args [i ] == nil {
78
95
nullBitmap [i / 8 ] |= 1 << (uint (i ) % 8 )
79
- paramTypes [i << 1 ] = MYSQL_TYPE_NULL
96
+ paramTypes [i ] = []byte {MYSQL_TYPE_NULL }
97
+ paramNames [i ] = []byte {0 } // length encoded, no name
98
+ paramFlags [i ] = []byte {0 }
80
99
continue
81
100
}
82
101
83
102
newParamBoundFlag = 1
84
103
85
104
switch v := args [i ].(type ) {
86
105
case int8 :
87
- paramTypes [i << 1 ] = MYSQL_TYPE_TINY
106
+ paramTypes [i ] = [] byte { MYSQL_TYPE_TINY }
88
107
paramValues [i ] = []byte {byte (v )}
89
108
case int16 :
90
- paramTypes [i << 1 ] = MYSQL_TYPE_SHORT
109
+ paramTypes [i ] = [] byte { MYSQL_TYPE_SHORT }
91
110
paramValues [i ] = Uint16ToBytes (uint16 (v ))
92
111
case int32 :
93
- paramTypes [i << 1 ] = MYSQL_TYPE_LONG
112
+ paramTypes [i ] = [] byte { MYSQL_TYPE_LONG }
94
113
paramValues [i ] = Uint32ToBytes (uint32 (v ))
95
114
case int :
96
- paramTypes [i << 1 ] = MYSQL_TYPE_LONGLONG
115
+ paramTypes [i ] = [] byte { MYSQL_TYPE_LONGLONG }
97
116
paramValues [i ] = Uint64ToBytes (uint64 (v ))
98
117
case int64 :
99
- paramTypes [i << 1 ] = MYSQL_TYPE_LONGLONG
118
+ paramTypes [i ] = [] byte { MYSQL_TYPE_LONGLONG }
100
119
paramValues [i ] = Uint64ToBytes (uint64 (v ))
101
120
case uint8 :
102
- paramTypes [i << 1 ] = MYSQL_TYPE_TINY
103
- paramTypes [( i << 1 ) + 1 ] = 0x80
121
+ paramTypes [i ] = [] byte { MYSQL_TYPE_TINY }
122
+ paramFlags [ i ] = [] byte { PARAM_UNSIGNED }
104
123
paramValues [i ] = []byte {v }
105
124
case uint16 :
106
- paramTypes [i << 1 ] = MYSQL_TYPE_SHORT
107
- paramTypes [( i << 1 ) + 1 ] = 0x80
125
+ paramTypes [i ] = [] byte { MYSQL_TYPE_SHORT }
126
+ paramFlags [ i ] = [] byte { PARAM_UNSIGNED }
108
127
paramValues [i ] = Uint16ToBytes (v )
109
128
case uint32 :
110
- paramTypes [i << 1 ] = MYSQL_TYPE_LONG
111
- paramTypes [( i << 1 ) + 1 ] = 0x80
129
+ paramTypes [i ] = [] byte { MYSQL_TYPE_LONG }
130
+ paramFlags [ i ] = [] byte { PARAM_UNSIGNED }
112
131
paramValues [i ] = Uint32ToBytes (v )
113
132
case uint :
114
- paramTypes [i << 1 ] = MYSQL_TYPE_LONGLONG
115
- paramTypes [( i << 1 ) + 1 ] = 0x80
133
+ paramTypes [i ] = [] byte { MYSQL_TYPE_LONGLONG }
134
+ paramFlags [ i ] = [] byte { PARAM_UNSIGNED }
116
135
paramValues [i ] = Uint64ToBytes (uint64 (v ))
117
136
case uint64 :
118
- paramTypes [i << 1 ] = MYSQL_TYPE_LONGLONG
119
- paramTypes [( i << 1 ) + 1 ] = 0x80
137
+ paramTypes [i ] = [] byte { MYSQL_TYPE_LONGLONG }
138
+ paramFlags [ i ] = [] byte { PARAM_UNSIGNED }
120
139
paramValues [i ] = Uint64ToBytes (v )
121
140
case bool :
122
- paramTypes [i << 1 ] = MYSQL_TYPE_TINY
141
+ paramTypes [i ] = [] byte { MYSQL_TYPE_TINY }
123
142
if v {
124
143
paramValues [i ] = []byte {1 }
125
144
} else {
126
145
paramValues [i ] = []byte {0 }
127
146
}
128
147
case float32 :
129
- paramTypes [i << 1 ] = MYSQL_TYPE_FLOAT
148
+ paramTypes [i ] = [] byte { MYSQL_TYPE_FLOAT }
130
149
paramValues [i ] = Uint32ToBytes (math .Float32bits (v ))
131
150
case float64 :
132
- paramTypes [i << 1 ] = MYSQL_TYPE_DOUBLE
151
+ paramTypes [i ] = [] byte { MYSQL_TYPE_DOUBLE }
133
152
paramValues [i ] = Uint64ToBytes (math .Float64bits (v ))
134
153
case string :
135
- paramTypes [i << 1 ] = MYSQL_TYPE_STRING
154
+ paramTypes [i ] = [] byte { MYSQL_TYPE_STRING }
136
155
paramValues [i ] = append (PutLengthEncodedInt (uint64 (len (v ))), v ... )
137
156
case []byte :
138
- paramTypes [i << 1 ] = MYSQL_TYPE_STRING
157
+ paramTypes [i ] = [] byte { MYSQL_TYPE_STRING }
139
158
paramValues [i ] = append (PutLengthEncodedInt (uint64 (len (v ))), v ... )
140
159
case json.RawMessage :
141
- paramTypes [i << 1 ] = MYSQL_TYPE_STRING
160
+ paramTypes [i ] = [] byte { MYSQL_TYPE_STRING }
142
161
paramValues [i ] = append (PutLengthEncodedInt (uint64 (len (v ))), v ... )
143
162
default :
144
163
return fmt .Errorf ("invalid argument type %T" , args [i ])
145
164
}
165
+ paramNames [i ] = []byte {0 } // length encoded, no name
166
+ if paramFlags [i ] == nil {
167
+ paramFlags [i ] = []byte {0 }
168
+ }
146
169
147
170
length += len (paramValues [i ])
148
171
}
172
+ for i , qa := range s .conn .queryAttributes {
173
+ tf := qa .TypeAndFlag ()
174
+ paramTypes [(i + paramsNum )] = []byte {tf [0 ]}
175
+ paramFlags [i + paramsNum ] = []byte {tf [1 ]}
176
+ paramValues [i + paramsNum ] = qa .ValueBytes ()
177
+ paramNames [i + paramsNum ] = PutLengthEncodedString ([]byte (qa .Name ))
178
+ }
149
179
150
180
data := utils .BytesBufferGet ()
151
181
defer func () {
@@ -159,25 +189,40 @@ func (s *Stmt) write(args ...interface{}) error {
159
189
data .WriteByte (COM_STMT_EXECUTE )
160
190
data .Write ([]byte {byte (s .id ), byte (s .id >> 8 ), byte (s .id >> 16 ), byte (s .id >> 24 )})
161
191
162
- //flag: CURSOR_TYPE_NO_CURSOR
163
- data .WriteByte (0x00 )
192
+ flags := CURSOR_TYPE_NO_CURSOR
193
+ if paramsNum > 0 {
194
+ flags |= PARAMETER_COUNT_AVAILABLE
195
+ }
196
+ data .WriteByte (flags )
164
197
165
198
//iteration-count, always 1
166
199
data .Write ([]byte {1 , 0 , 0 , 0 })
167
200
168
- if s .params > 0 {
169
- data .Write (nullBitmap )
170
-
171
- //new-params-bound-flag
172
- data .WriteByte (newParamBoundFlag )
173
-
174
- if newParamBoundFlag == 1 {
175
- //type of each parameter, length: num-params * 2
176
- data .Write (paramTypes )
177
-
178
- //value of each parameter
179
- for _ , v := range paramValues {
180
- data .Write (v )
201
+ if paramsNum > 0 || (s .conn .capability & CLIENT_QUERY_ATTRIBUTES > 0 && (flags & PARAMETER_COUNT_AVAILABLE > 0 )) {
202
+ if s .conn .capability & CLIENT_QUERY_ATTRIBUTES > 0 {
203
+ paramsNum += len (s .conn .queryAttributes )
204
+ data .Write (PutLengthEncodedInt (uint64 (paramsNum )))
205
+ }
206
+ if paramsNum > 0 {
207
+ data .Write (nullBitmap )
208
+
209
+ //new-params-bound-flag
210
+ data .WriteByte (newParamBoundFlag )
211
+
212
+ if newParamBoundFlag == 1 {
213
+ for i := 0 ; i < paramsNum ; i ++ {
214
+ data .Write (paramTypes [i ])
215
+ data .Write (paramFlags [i ])
216
+
217
+ if s .conn .capability & CLIENT_QUERY_ATTRIBUTES > 0 {
218
+ data .Write (paramNames [i ])
219
+ }
220
+ }
221
+
222
+ //value of each parameter
223
+ for _ , v := range paramValues {
224
+ data .Write (v )
225
+ }
181
226
}
182
227
}
183
228
}
0 commit comments