Skip to content

Commit ce0591d

Browse files
author
Mark aka walkingdevel
authored
mysql: allocate memory for each string and blob dynamically depending on its value length (#18214)
1 parent f833188 commit ce0591d

File tree

2 files changed

+45
-26
lines changed

2 files changed

+45
-26
lines changed

vlib/db/mysql/orm.v

Lines changed: 30 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,6 @@ module mysql
33
import orm
44
import time
55

6-
type Prims = f32 | f64 | i16 | i64 | i8 | int | string | u16 | u32 | u64 | u8
7-
8-
// sql expr
9-
106
// @select is used internally by V's ORM for processing `SELECT ` queries
117
pub fn (db Connection) @select(config orm.SelectConfig, data orm.QueryData, where orm.QueryData) ![][]orm.Primitive {
128
query := orm.orm_select_gen(config, '`', false, '?', 0, where)
@@ -28,8 +24,8 @@ pub fn (db Connection) @select(config orm.SelectConfig, data orm.QueryData, wher
2824
mut dataptr := []&u8{}
2925

3026
for i in 0 .. num_fields {
31-
f := unsafe { fields[i] }
32-
match unsafe { FieldType(f.@type) } {
27+
field := unsafe { fields[i] }
28+
match unsafe { FieldType(field.@type) } {
3329
.type_tiny {
3430
dataptr << unsafe { malloc(1) }
3531
}
@@ -51,38 +47,40 @@ pub fn (db Connection) @select(config orm.SelectConfig, data orm.QueryData, wher
5147
.type_time, .type_date, .type_datetime, .type_time2, .type_datetime2 {
5248
dataptr << unsafe { malloc(sizeof(C.MYSQL_TIME)) }
5349
}
54-
.type_string, .type_blob {
55-
dataptr << unsafe { malloc(512) }
56-
}
57-
.type_var_string {
58-
dataptr << unsafe { malloc(2) }
50+
.type_string, .type_var_string, .type_blob, .type_tiny_blob, .type_medium_blob,
51+
.type_long_blob {
52+
// Memory will be allocated later dynamically depending on the length of the value.
53+
dataptr << &u8(0)
5954
}
6055
else {
61-
return error('\'${unsafe { FieldType(f.@type) }}\' is not yet implemented. Please create a new issue at https://github.com/vlang/v/issues/new')
56+
return error('\'${unsafe { FieldType(field.@type) }}\' is not yet implemented. Please create a new issue at https://github.com/vlang/v/issues/new')
6257
}
6358
}
6459
}
6560

66-
lens := []u32{len: int(num_fields), init: 0}
67-
stmt.bind_res(fields, dataptr, lens, num_fields)
61+
lengths := []u32{len: int(num_fields), init: 0}
62+
stmt.bind_res(fields, dataptr, lengths, num_fields)
6863

69-
mut row := 0
7064
mut types := config.types.clone()
7165
mut field_types := []FieldType{}
7266
if config.is_count {
7367
types = [orm.type_idx['u64']]
7468
}
69+
// Map stores column indexes and their binds in order to extract values
70+
// for these columns separately, with individual memory allocation for each value.
71+
mut string_binds_map := map[int]C.MYSQL_BIND{}
7572

7673
for i, mut mysql_bind in stmt.res {
77-
f := unsafe { fields[i] }
78-
field_types << unsafe { FieldType(f.@type) }
74+
field := unsafe { fields[i] }
75+
field_type := unsafe { FieldType(field.@type) }
76+
field_types << field_type
77+
7978
match types[i] {
8079
orm.type_string {
81-
mysql_bind.buffer_type = C.MYSQL_TYPE_BLOB
82-
mysql_bind.buffer_length = FieldType.type_blob.get_len()
80+
string_binds_map[i] = mysql_bind
8381
}
8482
orm.time {
85-
match unsafe { FieldType(f.@type) } {
83+
match field_type {
8684
.type_long {
8785
mysql_bind.buffer_type = C.MYSQL_TYPE_LONG
8886
}
@@ -92,7 +90,7 @@ pub fn (db Connection) @select(config orm.SelectConfig, data orm.QueryData, wher
9290
}
9391
.type_string, .type_blob {}
9492
else {
95-
return error('Unknown type ${f.@type}')
93+
return error('Unknown type ${field.@type}')
9694
}
9795
}
9896
}
@@ -102,13 +100,23 @@ pub fn (db Connection) @select(config orm.SelectConfig, data orm.QueryData, wher
102100

103101
stmt.bind_result_buffer()!
104102
stmt.store_result()!
103+
105104
for {
106105
status = stmt.fetch_stmt()!
107106

108107
if status == 1 || status == 100 {
109108
break
110109
}
111-
row++
110+
111+
for index, mut bind in string_binds_map {
112+
string_length := lengths[index] + 1
113+
dataptr[index] = unsafe { malloc(string_length) }
114+
bind.buffer = dataptr[index]
115+
bind.buffer_length = string_length
116+
bind.length = unsafe { nil }
117+
118+
stmt.fetch_column(bind, index)!
119+
}
112120

113121
data_list := buffer_to_primitive(dataptr, types, field_types)!
114122
ret << data_list

vlib/db/mysql/stmt.c.v

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ fn C.mysql_stmt_bind_result(&C.MYSQL_STMT, &C.MYSQL_BIND) bool
6161
fn C.mysql_stmt_fetch(&C.MYSQL_STMT) int
6262
fn C.mysql_stmt_next_result(&C.MYSQL_STMT) int
6363
fn C.mysql_stmt_store_result(&C.MYSQL_STMT) int
64+
fn C.mysql_stmt_fetch_column(&C.MYSQL_STMT, &C.MYSQL_BIND, u32, u64) int
6465

6566
pub struct Stmt {
6667
stmt &C.MYSQL_STMT = &C.MYSQL_STMT(unsafe { nil })
@@ -248,14 +249,12 @@ pub fn (mut stmt Stmt) bind(typ int, buffer voidptr, buf_len u32) {
248249
}
249250

250251
// bind_res will store one result in the statement `stmt`
251-
pub fn (mut stmt Stmt) bind_res(fields &C.MYSQL_FIELD, dataptr []&u8, lens []u32, num_fields int) {
252+
pub fn (mut stmt Stmt) bind_res(fields &C.MYSQL_FIELD, dataptr []&u8, lengths []u32, num_fields int) {
252253
for i in 0 .. num_fields {
253-
len := unsafe { FieldType(fields[i].@type).get_len() }
254254
stmt.res << C.MYSQL_BIND{
255255
buffer_type: unsafe { fields[i].@type }
256256
buffer: dataptr[i]
257-
length: &lens[i]
258-
buffer_length: len
257+
length: &lengths[i]
259258
}
260259
}
261260
}
@@ -283,3 +282,15 @@ pub fn (mut stmt Stmt) store_result() ! {
283282
return stmt.error(res)
284283
}
285284
}
285+
286+
// fetch_column fetches one column from the current result set row.
287+
// `bind` provides the buffer where data should be placed.
288+
// It should be set up the same way as for `mysql_stmt_bind_result()`.
289+
// `column` indicates which column to fetch. The first column is numbered 0.
290+
pub fn (mut stmt Stmt) fetch_column(bind &C.MYSQL_BIND, column int) ! {
291+
result := C.mysql_stmt_fetch_column(stmt.stmt, bind, column, 0)
292+
293+
if result != 0 && stmt.get_error_msg() != '' {
294+
return stmt.error(result)
295+
}
296+
}

0 commit comments

Comments
 (0)