mysql/mysql_client.lua

1205 lines
28 KiB
Lua
Raw Normal View History

2021-04-29 21:47:39 +02:00
2021-04-29 21:50:40 +02:00
-- MySQL client protocol in Lua.
2021-04-29 21:51:58 +02:00
-- Written by Yichun Zhang (agentzh). BSD license.
2021-04-30 08:59:49 +02:00
-- Modified by Cosmin Apreutesei. Pulbic domain.
2021-04-29 21:24:55 +02:00
2021-08-18 19:36:08 +02:00
local ffi = require'ffi'
2021-04-29 21:24:55 +02:00
local bit = require'bit'
2021-08-18 19:36:08 +02:00
local sha1 = require'sha1'.sha1
local glue = require'glue'
2021-04-29 21:24:55 +02:00
local sub = string.sub
local strbyte = string.byte
local strchar = string.char
local format = string.format
local strrep = string.rep
local band = bit.band
local bxor = bit.bxor
local bor = bit.bor
local lshift = bit.lshift
local rshift = bit.rshift
local tohex = bit.tohex
local concat = table.concat
2021-08-18 19:36:08 +02:00
local buffer = glue.buffer
local index = glue.index
local repl = glue.repl
2021-04-29 21:24:55 +02:00
local ok, new_tab = pcall(require, 'table.new')
2021-04-30 08:59:49 +02:00
new_tab = ok and new_tab or function() return {} end
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
local mysql = {}
2021-04-29 21:24:55 +02:00
local COM_QUIT = 0x01
local COM_QUERY = 0x03
local CLIENT_SSL = 0x0800
local SERVER_MORE_RESULTS_EXISTS = 8
2021-08-18 19:36:08 +02:00
local collation_names = {
[ 1] = 'big5_chinese_ci',
[ 2] = 'latin2_czech_cs',
[ 3] = 'dec8_swedish_ci',
[ 4] = 'cp850_general_ci',
[ 5] = 'latin1_german1_ci',
[ 6] = 'hp8_english_ci',
[ 7] = 'koi8r_general_ci',
[ 8] = 'latin1_swedish_ci',
[ 9] = 'latin2_general_ci',
[ 10] = 'swe7_swedish_ci',
[ 11] = 'ascii_general_ci',
[ 12] = 'ujis_japanese_ci',
[ 13] = 'sjis_japanese_ci',
[ 14] = 'cp1251_bulgarian_ci',
[ 15] = 'latin1_danish_ci',
[ 16] = 'hebrew_general_ci',
[ 18] = 'tis620_thai_ci',
[ 19] = 'euckr_korean_ci',
[ 20] = 'latin7_estonian_cs',
[ 21] = 'latin2_hungarian_ci',
[ 22] = 'koi8u_general_ci',
[ 23] = 'cp1251_ukrainian_ci',
[ 24] = 'gb2312_chinese_ci',
[ 25] = 'greek_general_ci',
[ 26] = 'cp1250_general_ci',
[ 27] = 'latin2_croatian_ci',
[ 28] = 'gbk_chinese_ci',
[ 29] = 'cp1257_lithuanian_ci',
[ 30] = 'latin5_turkish_ci',
[ 31] = 'latin1_german2_ci',
[ 32] = 'armscii8_general_ci',
[ 33] = 'utf8_general_ci',
[ 34] = 'cp1250_czech_cs',
[ 35] = 'ucs2_general_ci',
[ 36] = 'cp866_general_ci',
[ 37] = 'keybcs2_general_ci',
[ 38] = 'macce_general_ci',
[ 39] = 'macroman_general_ci',
[ 40] = 'cp852_general_ci',
[ 41] = 'latin7_general_ci',
[ 42] = 'latin7_general_cs',
[ 43] = 'macce_bin',
[ 44] = 'cp1250_croatian_ci',
[ 45] = 'utf8mb4_general_ci',
[ 46] = 'utf8mb4_bin',
[ 47] = 'latin1_bin',
[ 48] = 'latin1_general_ci',
[ 49] = 'latin1_general_cs',
[ 50] = 'cp1251_bin',
[ 51] = 'cp1251_general_ci',
[ 52] = 'cp1251_general_cs',
[ 53] = 'macroman_bin',
[ 54] = 'utf16_general_ci',
[ 55] = 'utf16_bin',
[ 56] = 'utf16le_general_ci',
[ 57] = 'cp1256_general_ci',
[ 58] = 'cp1257_bin',
[ 59] = 'cp1257_general_ci',
[ 60] = 'utf32_general_ci',
[ 61] = 'utf32_bin',
[ 62] = 'utf16le_bin',
[ 63] = 'binary',
[ 64] = 'armscii8_bin',
[ 65] = 'ascii_bin',
[ 66] = 'cp1250_bin',
[ 67] = 'cp1256_bin',
[ 68] = 'cp866_bin',
[ 69] = 'dec8_bin',
[ 70] = 'greek_bin',
[ 71] = 'hebrew_bin',
[ 72] = 'hp8_bin',
[ 73] = 'keybcs2_bin',
[ 74] = 'koi8r_bin',
[ 75] = 'koi8u_bin',
[ 76] = 'utf8_tolower_ci',
[ 77] = 'latin2_bin',
[ 78] = 'latin5_bin',
[ 79] = 'latin7_bin',
[ 80] = 'cp850_bin',
[ 81] = 'cp852_bin',
[ 82] = 'swe7_bin',
[ 83] = 'utf8_bin',
[ 84] = 'big5_bin',
[ 85] = 'euckr_bin',
[ 86] = 'gb2312_bin',
[ 87] = 'gbk_bin',
[ 88] = 'sjis_bin',
[ 89] = 'tis620_bin',
[ 90] = 'ucs2_bin',
[ 91] = 'ujis_bin',
[ 92] = 'geostd8_general_ci',
[ 93] = 'geostd8_bin',
[ 94] = 'latin1_spanish_ci',
[ 95] = 'cp932_japanese_ci',
[ 96] = 'cp932_bin',
[ 97] = 'eucjpms_japanese_ci',
[ 98] = 'eucjpms_bin',
[ 99] = 'cp1250_polish_ci',
[101] = 'utf16_unicode_ci',
[102] = 'utf16_icelandic_ci',
[103] = 'utf16_latvian_ci',
[104] = 'utf16_romanian_ci',
[105] = 'utf16_slovenian_ci',
[106] = 'utf16_polish_ci',
[107] = 'utf16_estonian_ci',
[108] = 'utf16_spanish_ci',
[109] = 'utf16_swedish_ci',
[110] = 'utf16_turkish_ci',
[111] = 'utf16_czech_ci',
[112] = 'utf16_danish_ci',
[113] = 'utf16_lithuanian_ci',
[114] = 'utf16_slovak_ci',
[115] = 'utf16_spanish2_ci',
[116] = 'utf16_roman_ci',
[117] = 'utf16_persian_ci',
[118] = 'utf16_esperanto_ci',
[119] = 'utf16_hungarian_ci',
[120] = 'utf16_sinhala_ci',
[121] = 'utf16_german2_ci',
[122] = 'utf16_croatian_ci',
[123] = 'utf16_unicode_520_ci',
[124] = 'utf16_vietnamese_ci',
[128] = 'ucs2_unicode_ci',
[129] = 'ucs2_icelandic_ci',
[130] = 'ucs2_latvian_ci',
[131] = 'ucs2_romanian_ci',
[132] = 'ucs2_slovenian_ci',
[133] = 'ucs2_polish_ci',
[134] = 'ucs2_estonian_ci',
[135] = 'ucs2_spanish_ci',
[136] = 'ucs2_swedish_ci',
[137] = 'ucs2_turkish_ci',
[138] = 'ucs2_czech_ci',
[139] = 'ucs2_danish_ci',
[140] = 'ucs2_lithuanian_ci',
[141] = 'ucs2_slovak_ci',
[142] = 'ucs2_spanish2_ci',
[143] = 'ucs2_roman_ci',
[144] = 'ucs2_persian_ci',
[145] = 'ucs2_esperanto_ci',
[146] = 'ucs2_hungarian_ci',
[147] = 'ucs2_sinhala_ci',
[148] = 'ucs2_german2_ci',
[149] = 'ucs2_croatian_ci',
[150] = 'ucs2_unicode_520_ci',
[151] = 'ucs2_vietnamese_ci',
[159] = 'ucs2_general_mysql500_ci',
[160] = 'utf32_unicode_ci',
[161] = 'utf32_icelandic_ci',
[162] = 'utf32_latvian_ci',
[163] = 'utf32_romanian_ci',
[164] = 'utf32_slovenian_ci',
[165] = 'utf32_polish_ci',
[166] = 'utf32_estonian_ci',
[167] = 'utf32_spanish_ci',
[168] = 'utf32_swedish_ci',
[169] = 'utf32_turkish_ci',
[170] = 'utf32_czech_ci',
[171] = 'utf32_danish_ci',
[172] = 'utf32_lithuanian_ci',
[173] = 'utf32_slovak_ci',
[174] = 'utf32_spanish2_ci',
[175] = 'utf32_roman_ci',
[176] = 'utf32_persian_ci',
[177] = 'utf32_esperanto_ci',
[178] = 'utf32_hungarian_ci',
[179] = 'utf32_sinhala_ci',
[180] = 'utf32_german2_ci',
[181] = 'utf32_croatian_ci',
[182] = 'utf32_unicode_520_ci',
[183] = 'utf32_vietnamese_ci',
[192] = 'utf8_unicode_ci',
[193] = 'utf8_icelandic_ci',
[194] = 'utf8_latvian_ci',
[195] = 'utf8_romanian_ci',
[196] = 'utf8_slovenian_ci',
[197] = 'utf8_polish_ci',
[198] = 'utf8_estonian_ci',
[199] = 'utf8_spanish_ci',
[200] = 'utf8_swedish_ci',
[201] = 'utf8_turkish_ci',
[202] = 'utf8_czech_ci',
[203] = 'utf8_danish_ci',
[204] = 'utf8_lithuanian_ci',
[205] = 'utf8_slovak_ci',
[206] = 'utf8_spanish2_ci',
[207] = 'utf8_roman_ci',
[208] = 'utf8_persian_ci',
[209] = 'utf8_esperanto_ci',
[210] = 'utf8_hungarian_ci',
[211] = 'utf8_sinhala_ci',
[212] = 'utf8_german2_ci',
[213] = 'utf8_croatian_ci',
[214] = 'utf8_unicode_520_ci',
[215] = 'utf8_vietnamese_ci',
[223] = 'utf8_general_mysql500_ci',
[224] = 'utf8mb4_unicode_ci',
[225] = 'utf8mb4_icelandic_ci',
[226] = 'utf8mb4_latvian_ci',
[227] = 'utf8mb4_romanian_ci',
[228] = 'utf8mb4_slovenian_ci',
[229] = 'utf8mb4_polish_ci',
[230] = 'utf8mb4_estonian_ci',
[231] = 'utf8mb4_spanish_ci',
[232] = 'utf8mb4_swedish_ci',
[233] = 'utf8mb4_turkish_ci',
[234] = 'utf8mb4_czech_ci',
[235] = 'utf8mb4_danish_ci',
[236] = 'utf8mb4_lithuanian_ci',
[237] = 'utf8mb4_slovak_ci',
[238] = 'utf8mb4_spanish2_ci',
[239] = 'utf8mb4_roman_ci',
[240] = 'utf8mb4_persian_ci',
[241] = 'utf8mb4_esperanto_ci',
[242] = 'utf8mb4_hungarian_ci',
[243] = 'utf8mb4_sinhala_ci',
[244] = 'utf8mb4_german2_ci',
[245] = 'utf8mb4_croatian_ci',
[246] = 'utf8mb4_unicode_520_ci',
[247] = 'utf8mb4_vietnamese_ci',
[248] = 'gb18030_chinese_ci',
[249] = 'gb18030_bin',
[250] = 'gb18030_unicode_520_ci',
[255] = 'utf8mb4_0900_ai_ci',
[256] = 'utf8mb4_de_pb_0900_ai_ci',
[257] = 'utf8mb4_is_0900_ai_ci',
[258] = 'utf8mb4_lv_0900_ai_ci',
[259] = 'utf8mb4_ro_0900_ai_ci',
[260] = 'utf8mb4_sl_0900_ai_ci',
[261] = 'utf8mb4_pl_0900_ai_ci',
[262] = 'utf8mb4_et_0900_ai_ci',
[263] = 'utf8mb4_es_0900_ai_ci',
[264] = 'utf8mb4_sv_0900_ai_ci',
[265] = 'utf8mb4_tr_0900_ai_ci',
[266] = 'utf8mb4_cs_0900_ai_ci',
[267] = 'utf8mb4_da_0900_ai_ci',
[268] = 'utf8mb4_lt_0900_ai_ci',
[269] = 'utf8mb4_sk_0900_ai_ci',
[270] = 'utf8mb4_es_trad_0900_ai_ci',
[271] = 'utf8mb4_la_0900_ai_ci',
[273] = 'utf8mb4_eo_0900_ai_ci',
[274] = 'utf8mb4_hu_0900_ai_ci',
[275] = 'utf8mb4_hr_0900_ai_ci',
[277] = 'utf8mb4_vi_0900_ai_ci',
[278] = 'utf8mb4_0900_as_cs',
[279] = 'utf8mb4_de_pb_0900_as_cs',
[280] = 'utf8mb4_is_0900_as_cs',
[281] = 'utf8mb4_lv_0900_as_cs',
[282] = 'utf8mb4_ro_0900_as_cs',
[283] = 'utf8mb4_sl_0900_as_cs',
[284] = 'utf8mb4_pl_0900_as_cs',
[285] = 'utf8mb4_et_0900_as_cs',
[286] = 'utf8mb4_es_0900_as_cs',
[287] = 'utf8mb4_sv_0900_as_cs',
[288] = 'utf8mb4_tr_0900_as_cs',
[289] = 'utf8mb4_cs_0900_as_cs',
[290] = 'utf8mb4_da_0900_as_cs',
[291] = 'utf8mb4_lt_0900_as_cs',
[292] = 'utf8mb4_sk_0900_as_cs',
[293] = 'utf8mb4_es_trad_0900_as_cs',
[294] = 'utf8mb4_la_0900_as_cs',
[296] = 'utf8mb4_eo_0900_as_cs',
[297] = 'utf8mb4_hu_0900_as_cs',
[298] = 'utf8mb4_hr_0900_as_cs',
[300] = 'utf8mb4_vi_0900_as_cs',
[303] = 'utf8mb4_ja_0900_as_cs',
[304] = 'utf8mb4_ja_0900_as_cs_ks',
[305] = 'utf8mb4_0900_as_ci',
[306] = 'utf8mb4_ru_0900_ai_ci',
[307] = 'utf8mb4_ru_0900_as_cs',
[308] = 'utf8mb4_zh_0900_as_cs',
[309] = 'utf8mb4_0900_bin',
}
local collation_codes = index(collation_names)
local default_collations = {
big5 = 'big5_chinese_ci',
dec8 = 'dec8_swedish_ci',
cp850 = 'cp850_general_ci',
hp8 = 'hp8_english_ci',
koi8r = 'koi8r_general_ci',
latin1 = 'latin1_swedish_ci',
latin2 = 'latin2_general_ci',
swe7 = 'swe7_swedish_ci',
ascii = 'ascii_general_ci',
ujis = 'ujis_japanese_ci',
sjis = 'sjis_japanese_ci',
hebrew = 'hebrew_general_ci',
tis620 = 'tis620_thai_ci',
euckr = 'euckr_korean_ci',
koi8u = 'koi8u_general_ci',
gb2312 = 'gb2312_chinese_ci',
greek = 'greek_general_ci',
cp1250 = 'cp1250_general_ci',
gbk = 'gbk_chinese_ci',
latin5 = 'latin5_turkish_ci',
armscii8 = 'armscii8_general_ci',
utf8 = 'utf8_general_ci',
ucs2 = 'ucs2_general_ci',
cp866 = 'cp866_general_ci',
keybcs2 = 'keybcs2_general_ci',
macce = 'macce_general_ci',
macroman = 'macroman_general_ci',
cp852 = 'cp852_general_ci',
latin7 = 'latin7_general_ci',
cp1251 = 'cp1251_general_ci',
utf16 = 'utf16_general_ci',
utf16le = 'utf16le_general_ci',
cp1256 = 'cp1256_general_ci',
cp1257 = 'cp1257_general_ci',
utf32 = 'utf32_general_ci',
binary = 'binary',
geostd8 = 'geostd8_general_ci',
cp932 = 'cp932_japanese_ci',
eucjpms = 'eucjpms_japanese_ci',
gb18030 = 'gb18030_chinese_ci',
utf8mb4 = 'utf8mb4_0900_ai_ci',
}
local buffer_type_names = {
[ 0] = 'decimal',
[ 1] = 'tiny',
[ 2] = 'short',
[ 3] = 'long',
[ 4] = 'float',
[ 5] = 'double',
[ 6] = 'null',
[ 7] = 'timestamp',
[ 8] = 'longlong',
[ 9] = 'int24',
[ 10] = 'date',
[ 11] = 'time',
[ 12] = 'datetime',
[ 13] = 'year',
[ 15] = 'varchar',
[ 16] = 'bit',
[246] = 'newdecimal',
[247] = 'enum',
[248] = 'set',
[249] = 'tiny_blob',
[250] = 'medium_blob',
[251] = 'long_blob',
[252] = 'blob',
[253] = 'var_string',
[254] = 'string',
[255] = 'geometry',
}
local type_names = {
tiny = 'tinyint',
short = 'shortint',
long = 'int',
int24 = 'mediumint',
longlong = 'bigint',
newdecimal = 'decimal',
}
local bin_type_names = {
tiny_blob = 'tinyblob',
medium_blob = 'mediumblob',
long_blob = 'longblob',
blob = 'blob',
var_string = 'varbinary',
string = 'binary',
}
local text_type_names = {
tiny_blob = 'tinytext',
medium_blob = 'mediumtext',
long_blob = 'longtext',
blob = 'text',
var_string = 'varchar',
string = 'char',
2021-04-29 21:24:55 +02:00
}
2021-04-30 08:59:49 +02:00
local conn = {}
local mt = {__index = conn}
2021-04-29 21:24:55 +02:00
-- mysql field value type converters
2021-08-18 19:36:08 +02:00
local converters = {
tinyint = tonumber,
shortint = tonumber,
mediumint = tonumber,
int = tonumber,
bigint = tonumber,
year = tonumber,
float = tonumber,
double = tonumber,
}
2021-04-29 21:24:55 +02:00
local function _get_byte2(data, i)
2021-04-30 08:59:49 +02:00
local a, b = strbyte(data, i, i + 1)
return bor(a, lshift(b, 8)), i + 2
2021-04-29 21:24:55 +02:00
end
local function _get_byte3(data, i)
2021-04-30 08:59:49 +02:00
local a, b, c = strbyte(data, i, i + 2)
return bor(a, lshift(b, 8), lshift(c, 16)), i + 3
2021-04-29 21:24:55 +02:00
end
local function _get_byte4(data, i)
2021-04-30 08:59:49 +02:00
local a, b, c, d = strbyte(data, i, i + 3)
return bor(a, lshift(b, 8), lshift(c, 16), lshift(d, 24)), i + 4
2021-04-29 21:24:55 +02:00
end
local function _get_byte8(data, i)
2021-04-30 08:59:49 +02:00
local a, b, c, d, e, f, g, h = strbyte(data, i, i + 7)
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
-- XXX workaround for the lack of 64-bit support in bitop:
local lo = bor(a, lshift(b, 8), lshift(c, 16), lshift(d, 24))
local hi = bor(e, lshift(f, 8), lshift(g, 16), lshift(h, 24))
return lo + hi * 4294967296, i + 8
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
-- return bor(a, lshift(b, 8), lshift(c, 16), lshift(d, 24), lshift(e, 32),
-- lshift(f, 40), lshift(g, 48), lshift(h, 56)), i + 8
2021-04-29 21:24:55 +02:00
end
local function _set_byte2(n)
2021-04-30 08:59:49 +02:00
return strchar(band(n, 0xff), band(rshift(n, 8), 0xff))
2021-04-29 21:24:55 +02:00
end
local function _set_byte3(n)
2021-04-30 08:59:49 +02:00
return strchar(band(n, 0xff),
band(rshift(n, 8), 0xff),
band(rshift(n, 16), 0xff))
2021-04-29 21:24:55 +02:00
end
local function _set_byte4(n)
2021-04-30 08:59:49 +02:00
return strchar(band(n, 0xff),
band(rshift(n, 8), 0xff),
band(rshift(n, 16), 0xff),
band(rshift(n, 24), 0xff))
2021-04-29 21:24:55 +02:00
end
local function _from_cstring(data, i)
2021-08-18 19:36:08 +02:00
local last = data:find('\0', i, true)
2021-04-30 08:59:49 +02:00
if not last then
return nil, nil
end
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
return sub(data, i, last - 1), last + 1
2021-04-29 21:24:55 +02:00
end
local function _to_cstring(data)
2021-04-30 08:59:49 +02:00
return data .. '\0'
2021-04-29 21:24:55 +02:00
end
local function _to_binary_coded_string(data)
2021-04-30 08:59:49 +02:00
return strchar(#data) .. data
2021-04-29 21:24:55 +02:00
end
local function _dump(data)
2021-04-30 08:59:49 +02:00
local len = #data
local bytes = new_tab(len, 0)
for i = 1, len do
bytes[i] = format('%x', strbyte(data, i))
end
return concat(bytes, ' ')
2021-04-29 21:24:55 +02:00
end
local function _dumphex(data)
2021-04-30 08:59:49 +02:00
local len = #data
local bytes = new_tab(len, 0)
for i = 1, len do
bytes[i] = tohex(strbyte(data, i), 2)
end
return concat(bytes, ' ')
2021-04-29 21:24:55 +02:00
end
local function _compute_token(password, scramble)
2021-04-30 08:59:49 +02:00
if password == '' then
return ''
end
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
local stage1 = sha1(password)
local stage2 = sha1(stage1)
local stage3 = sha1(scramble .. stage2)
local n = #stage1
local bytes = new_tab(n, 0)
for i = 1, n do
bytes[i] = strchar(bxor(strbyte(stage3, i), strbyte(stage1, i)))
end
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
return concat(bytes)
2021-04-29 21:24:55 +02:00
end
local function _send_packet(self, req, size)
2021-04-30 08:59:49 +02:00
local sock = self.sock
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
self.packet_no = self.packet_no + 1
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
-- print('packet no: ', self.packet_no)
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
local packet = _set_byte3(size) .. strchar(band(self.packet_no, 255)) .. req
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
-- print('sending packet: ', _dump(packet))
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
-- print('sending packet... of size ' .. #packet)
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
return sock:send(packet)
2021-04-29 21:24:55 +02:00
end
2021-04-30 00:27:35 +02:00
local function _recv(self, sz)
2021-04-30 08:59:49 +02:00
local buf = self.buf
if not buf then
2021-08-18 19:36:08 +02:00
buf = buffer'char[?]'
2021-04-30 08:59:49 +02:00
self.buf = buf
end
local buf = buf(sz)
2021-05-10 20:31:23 +02:00
local ok, err = self.sock:recvall(buf, sz)
if not ok then return nil, err end
return ffi.string(buf, sz)
2021-04-30 00:27:35 +02:00
end
2021-04-29 21:24:55 +02:00
local function _recv_packet(self)
2021-04-30 08:59:49 +02:00
local sock = self.sock
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
local data, err = _recv(self, 4) -- packet header
if not data then
return nil, nil, 'failed to receive packet header: ' .. err
end
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
--print('packet header: ', _dump(data))
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
local len, pos = _get_byte3(data, 1)
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
--print('packet length: ', len)
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
if len == 0 then
return nil, nil, 'empty packet'
end
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
if len > self._max_packet_size then
return nil, nil, 'packet size too big: ' .. len
end
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
local num = strbyte(data, pos)
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
--print('recv packet: packet no: ', num)
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
self.packet_no = num
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
data, err = _recv(self, len)
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
--print('receive returned')
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
if not data then
return nil, nil, 'failed to read packet content: ' .. err
end
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
--print('packet content: ', _dump(data))
--print('packet content (ascii): ', data)
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
local field_count = strbyte(data, 1)
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
local typ
if field_count == 0x00 then
typ = 'OK'
elseif field_count == 0xff then
typ = 'ERR'
elseif field_count == 0xfe then
typ = 'EOF'
else
typ = 'DATA'
end
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
return data, typ
2021-04-29 21:24:55 +02:00
end
local function _from_length_coded_bin(data, pos)
2021-04-30 08:59:49 +02:00
local first = strbyte(data, pos)
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
--print('LCB: first: ', first)
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
if not first then
return nil, pos
end
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
if first >= 0 and first <= 250 then
return first, pos + 1
end
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
if first == 251 then
2021-08-18 19:36:08 +02:00
return nil, pos + 1
2021-04-30 08:59:49 +02:00
end
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
if first == 252 then
pos = pos + 1
return _get_byte2(data, pos)
end
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
if first == 253 then
pos = pos + 1
return _get_byte3(data, pos)
end
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
if first == 254 then
pos = pos + 1
return _get_byte8(data, pos)
end
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
return nil, pos + 1
2021-04-29 21:24:55 +02:00
end
2021-07-12 21:46:51 +02:00
local function _from_length_coded_str(data, pos)
2021-04-30 08:59:49 +02:00
local len
len, pos = _from_length_coded_bin(data, pos)
2021-08-18 19:36:08 +02:00
if not len then
return nil, pos
2021-04-30 08:59:49 +02:00
end
2021-07-12 21:46:51 +02:00
return sub(data, pos, pos + len - 1), pos + len
2021-04-29 21:24:55 +02:00
end
local function _parse_ok_packet(packet)
2021-04-30 08:59:49 +02:00
local res = new_tab(0, 5)
local pos
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
res.affected_rows, pos = _from_length_coded_bin(packet, 2)
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
--print('affected rows: ', res.affected_rows, ', pos:', pos)
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
res.insert_id, pos = _from_length_coded_bin(packet, pos)
2021-04-29 21:24:55 +02:00
2021-08-18 19:36:08 +02:00
if res.insert_id == 0 then
res.insert_id = nil
end
2021-04-30 08:59:49 +02:00
--print('insert id: ', res.insert_id, ', pos:', pos)
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
res.server_status, pos = _get_byte2(packet, pos)
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
--print('server status: ', res.server_status, ', pos:', pos)
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
res.warning_count, pos = _get_byte2(packet, pos)
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
--print('warning count: ', res.warning_count, ', pos: ', pos)
2021-04-29 21:24:55 +02:00
2021-08-18 19:36:08 +02:00
res.message = _from_length_coded_str(packet, pos)
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
--print('message: ', res.message, ', pos:', pos)
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
return res
2021-04-29 21:24:55 +02:00
end
local function _parse_eof_packet(packet)
2021-04-30 08:59:49 +02:00
local pos = 2
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
local warning_count, pos = _get_byte2(packet, pos)
local status_flags = _get_byte2(packet, pos)
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
return warning_count, status_flags
2021-04-29 21:24:55 +02:00
end
local function _parse_err_packet(packet)
2021-04-30 08:59:49 +02:00
local errno, pos = _get_byte2(packet, 2)
local marker = sub(packet, pos, pos)
local sqlstate
if marker == '#' then
-- with sqlstate
pos = pos + 1
sqlstate = sub(packet, pos, pos + 5 - 1)
pos = pos + 5
end
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
local message = sub(packet, pos)
2021-08-18 19:36:08 +02:00
message = message:gsub('You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ', 'Syntax error: ')
2021-04-30 08:59:49 +02:00
return errno, message, sqlstate
2021-04-29 21:24:55 +02:00
end
local function _parse_result_set_header_packet(packet)
2021-04-30 08:59:49 +02:00
local field_count, pos = _from_length_coded_bin(packet, 1)
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
local extra
extra = _from_length_coded_bin(packet, pos)
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
return field_count, extra
2021-04-29 21:24:55 +02:00
end
local NOT_NULL_FLAG = 1
local PRI_KEY_FLAG = 2
local UNIQUE_KEY_FLAG = 4
local UNSIGNED_FLAG = 32
local AUTO_INCREMENT_FLAG = 512
2021-07-12 21:46:51 +02:00
local function _parse_field(data, pos)
local s, pos = _from_length_coded_str(data, pos)
2021-08-18 19:36:08 +02:00
s = s and s ~= '' and s:lower() or nil
2021-07-12 21:46:51 +02:00
return s, pos
end
2021-04-29 21:24:55 +02:00
local function _parse_field_packet(data)
2021-04-30 08:59:49 +02:00
local col = new_tab(0, 2)
local pos
2021-07-12 21:46:51 +02:00
col.catalog, pos = _parse_field(data, 1)
col.schema, pos = _parse_field(data, pos)
col.table, pos = _parse_field(data, pos)
2021-08-18 19:36:08 +02:00
col.origin_table, pos = _parse_field(data, pos)
2021-07-12 21:46:51 +02:00
col.name, pos = _parse_field(data, pos)
2021-08-18 19:36:08 +02:00
col.origin_name, pos = _parse_field(data, pos)
2021-04-30 08:59:49 +02:00
pos = pos + 1 -- ignore the filler
2021-08-18 19:36:08 +02:00
local collation, pos = _get_byte2(data, pos)
2021-04-30 08:59:49 +02:00
col.length, pos = _get_byte4(data, pos)
2021-08-18 19:36:08 +02:00
local buffer_type = buffer_type_names[strbyte(data, pos)]
local text_type_names = collation == 63 and bin_type_names or text_type_names
col.type = text_type_names[buffer_type]
if col.type then
col.collation = collation_names[collation]
col.charset = col.collation and col.collation:match'^[^_]+'
else
col.type = type_names[buffer_type] or buffer_type
end
2021-04-30 08:59:49 +02:00
pos = pos + 1
2021-08-18 19:36:08 +02:00
local flags, pos = _get_byte2(data, pos)
col.not_null = band(flags, NOT_NULL_FLAG ) ~= 0 or nil
col.pri_key = band(flags, PRI_KEY_FLAG ) ~= 0 or nil
col.unique_key = band(flags, UNIQUE_KEY_FLAG ) ~= 0 or nil
col.unsigned = band(flags, UNSIGNED_FLAG ) ~= 0 or nil
col.auto_increment = band(flags, AUTO_INCREMENT_FLAG) ~= 0 or nil
2021-04-30 08:59:49 +02:00
col.decimals = strbyte(data, pos)
pos = pos + 1
2021-08-18 19:36:08 +02:00
col.default = repl(sub(data, pos + 2), '', nil)
2021-04-30 08:59:49 +02:00
return col
2021-04-29 21:24:55 +02:00
end
2021-08-18 19:36:08 +02:00
local function _parse_row_data_packet(data, cols, compact, to_array, null_value)
2021-04-30 08:59:49 +02:00
local pos = 1
local ncols = #cols
local row
2021-08-18 19:36:08 +02:00
if not to_array then
if compact then
row = new_tab(ncols, 0)
else
row = new_tab(0, ncols)
end
2021-04-30 08:59:49 +02:00
end
for i = 1, ncols do
local value
value, pos = _from_length_coded_str(data, pos)
local col = cols[i]
local typ = col.type
local name = col.name
--print('row field value: ', value, ', type: ', typ)
2021-08-18 19:36:08 +02:00
if value ~= nil then
2021-04-30 08:59:49 +02:00
local conv = converters[typ]
if conv then
value = conv(value)
end
2021-08-18 19:36:08 +02:00
else
value = null_value
end
if to_array then
return value
2021-04-30 08:59:49 +02:00
end
if compact then
row[i] = value
2021-08-18 19:36:08 +02:00
else
2021-04-30 08:59:49 +02:00
row[name] = value
end
end
return row
2021-04-29 21:24:55 +02:00
end
local function _recv_field_packet(self)
2021-04-30 08:59:49 +02:00
local packet, typ, err = _recv_packet(self)
if not packet then
return nil, err
end
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
if typ == 'ERR' then
local errno, msg, sqlstate = _parse_err_packet(packet)
return nil, msg, errno, sqlstate
end
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
if typ ~= 'DATA' then
return nil, 'bad field packet type: ' .. typ
end
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
-- typ == 'DATA'
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
return _parse_field_packet(packet)
2021-04-29 21:24:55 +02:00
end
2021-04-30 08:59:49 +02:00
function mysql.new(self, opt)
local tcp = opt and opt.tcp or require'sock'.tcp
local sock, err = tcp()
if not sock then
return nil, err
end
return setmetatable({ sock = sock }, mt)
2021-04-29 21:24:55 +02:00
end
2021-04-30 08:59:49 +02:00
function conn:connect(opts)
local sock = self.sock
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
local max_packet_size = opts.max_packet_size
if not max_packet_size then
max_packet_size = 1024 * 1024 -- default 1 MB
end
self._max_packet_size = max_packet_size
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
local ok, err
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
local database = opts.database or ''
local user = opts.user or ''
2021-04-29 21:24:55 +02:00
2021-08-18 19:36:08 +02:00
local collation = 0 --default
if opts.collation then
collation = assert(collation_codes[opts.collation], 'invalid collation')
elseif opts.charset then
collation = assert(default_collations[opts.charset], 'invalid charset')
collation = assert(collation_codes[collation])
2021-04-30 08:59:49 +02:00
end
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
local host = opts.host
local port = opts.port or 3306
2021-05-12 23:19:24 +02:00
ok, err, errcode = sock:connect(host, port)
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
if not ok then
2021-05-12 23:19:24 +02:00
return nil, err, errcode
2021-04-30 08:59:49 +02:00
end
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
local packet, typ, err = _recv_packet(self)
if not packet then
return nil, err
end
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
if typ == 'ERR' then
local errno, msg, sqlstate = _parse_err_packet(packet)
return nil, msg, errno, sqlstate
end
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
self.protocol_ver = strbyte(packet)
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
--print('protocol version: ', self.protocol_ver)
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
local server_ver, pos = _from_cstring(packet, 2)
if not server_ver then
return nil, 'bad handshake initialization packet: bad server version'
end
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
--print('server version: ', server_ver)
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
self._server_ver = server_ver
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
local thread_id, pos = _get_byte4(packet, pos)
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
--print('thread id: ', thread_id)
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
local scramble = sub(packet, pos, pos + 8 - 1)
if not scramble then
return nil, '1st part of scramble not found'
end
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
pos = pos + 9 -- skip filler
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
-- two lower bytes
local capabilities -- server capabilities
capabilities, pos = _get_byte2(packet, pos)
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
-- print(format('server capabilities: %#x', capabilities))
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
self._server_lang = strbyte(packet, pos)
pos = pos + 1
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
--print('server lang: ', self._server_lang)
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
self._server_status, pos = _get_byte2(packet, pos)
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
--print('server status: ', self._server_status)
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
local more_capabilities
more_capabilities, pos = _get_byte2(packet, pos)
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
capabilities = bor(capabilities, lshift(more_capabilities, 16))
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
--print('server capabilities: ', capabilities)
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
-- local len = strbyte(packet, pos)
local len = 21 - 8 - 1
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
--print('scramble len: ', len)
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
pos = pos + 1 + 10
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
local scramble_part2 = sub(packet, pos, pos + len - 1)
if not scramble_part2 then
return nil, '2nd part of scramble not found'
end
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
scramble = scramble .. scramble_part2
--print('scramble: ', _dump(scramble))
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
local client_flags = 0x3f7cf;
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
local ssl_verify = opts.ssl_verify
local use_ssl = opts.ssl or ssl_verify
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
if use_ssl then
if band(capabilities, CLIENT_SSL) == 0 then
return nil, 'ssl disabled on server'
end
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
-- send a SSL Request Packet
local req = _set_byte4(bor(client_flags, CLIENT_SSL))
.. _set_byte4(self._max_packet_size)
2021-08-18 19:36:08 +02:00
.. strchar(collation)
2021-04-30 08:59:49 +02:00
.. strrep('\0', 23)
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
local packet_len = 4 + 4 + 1 + 23
local bytes, err = _send_packet(self, req, packet_len)
if not bytes then
return nil, 'failed to send client authentication packet: ' .. err
end
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
local ok, err = sock:sslhandshake(false, nil, ssl_verify)
if not ok then
return nil, 'failed to do ssl handshake: ' .. (err or '')
end
end
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
local password = opts.password or ''
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
local token = _compute_token(password, scramble)
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
--print('token: ', _dump(token))
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
local req = _set_byte4(client_flags)
.. _set_byte4(self._max_packet_size)
2021-08-18 19:36:08 +02:00
.. strchar(collation)
2021-04-30 08:59:49 +02:00
.. strrep('\0', 23)
.. _to_cstring(user)
.. _to_binary_coded_string(token)
.. _to_cstring(database)
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
local packet_len = 4 + 4 + 1 + 23 + #user + 1
+ #token + 1 + #database + 1
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
-- print('packet content length: ', packet_len)
-- print('packet content: ', _dump(concat(req, '')))
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
local bytes, err = _send_packet(self, req, packet_len)
if not bytes then
return nil, 'failed to send client authentication packet: ' .. err
end
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
--print('packet sent ', bytes, ' bytes')
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
local packet, typ, err = _recv_packet(self)
if not packet then
return nil, 'failed to receive the result packet: ' .. err
end
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
if typ == 'ERR' then
local errno, msg, sqlstate = _parse_err_packet(packet)
return nil, msg, errno, sqlstate
end
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
if typ == 'EOF' then
return nil, 'old pre-4.1 authentication protocol not supported'
end
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
if typ ~= 'OK' then
return nil, 'bad packet type: ' .. typ
end
2021-04-29 21:24:55 +02:00
2021-08-18 19:36:08 +02:00
self.state = 'ready'
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
return 1
2021-04-29 21:24:55 +02:00
end
2021-04-30 08:59:49 +02:00
function conn:close()
local sock = assert(self.sock)
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
self.state = nil
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
local bytes, err = _send_packet(self, strchar(COM_QUIT), 1)
if not bytes then
return nil, err
end
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
return sock:close()
2021-04-29 21:24:55 +02:00
end
2021-04-30 08:59:49 +02:00
function conn:server_ver()
return self._server_ver
2021-04-29 21:24:55 +02:00
end
2021-04-30 08:59:49 +02:00
function conn:send_query(query)
2021-08-18 19:36:08 +02:00
assert(self.state == 'ready')
2021-04-30 08:59:49 +02:00
local sock = assert(self.sock)
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
self.packet_no = -1
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
local cmd_packet = strchar(COM_QUERY) .. query
local packet_len = 1 + #query
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
local bytes, err = _send_packet(self, cmd_packet, packet_len)
if not bytes then
return nil, err
end
2021-04-29 21:24:55 +02:00
2021-08-18 19:36:08 +02:00
self.state = 'read'
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
--print('packet sent ', bytes, ' bytes')
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
return bytes
2021-04-29 21:24:55 +02:00
end
2021-08-18 19:36:08 +02:00
function conn:read_result(opt)
assert(self.state == 'read')
2021-04-30 08:59:49 +02:00
local sock = assert(self.sock)
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
local packet, typ, err = _recv_packet(self)
if not packet then
return nil, err
end
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
if typ == 'ERR' then
2021-08-18 19:36:08 +02:00
self.state = 'ready'
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
local errno, msg, sqlstate = _parse_err_packet(packet)
return nil, msg, errno, sqlstate
end
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
if typ == 'OK' then
local res = _parse_ok_packet(packet)
if res and band(res.server_status, SERVER_MORE_RESULTS_EXISTS) ~= 0 then
return res, 'again'
end
2021-04-29 21:24:55 +02:00
2021-08-18 19:36:08 +02:00
self.state = 'ready'
2021-04-30 08:59:49 +02:00
return res
end
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
if typ ~= 'DATA' then
2021-08-18 19:36:08 +02:00
self.state = 'ready'
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
return nil, 'packet type ' .. typ .. ' not supported'
end
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
-- typ == 'DATA'
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
--print('read the result set header packet')
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
local field_count, extra = _parse_result_set_header_packet(packet)
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
--print('field count: ', field_count)
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
local cols = new_tab(field_count, 0)
for i = 1, field_count do
local col, err, errno, sqlstate = _recv_field_packet(self)
if not col then
return nil, err, errno, sqlstate
end
2021-04-29 21:24:55 +02:00
2021-08-18 19:36:08 +02:00
col.index = i
2021-04-30 08:59:49 +02:00
cols[i] = col
2021-08-18 19:36:08 +02:00
cols[col.name] = col
2021-04-30 08:59:49 +02:00
end
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
local packet, typ, err = _recv_packet(self)
if not packet then
return nil, err
end
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
if typ ~= 'EOF' then
return nil, 'unexpected packet type ' .. typ .. ' while eof packet is '
.. 'expected'
end
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
-- typ == 'EOF'
2021-04-29 21:24:55 +02:00
2021-08-18 19:36:08 +02:00
local compact = opt and opt.compact
local to_array = opt and opt.to_array and #cols == 1
local null_value = opt and opt.null_value
local rows = new_tab(4, 0)
2021-04-30 08:59:49 +02:00
local i = 0
while true do
--print('reading a row')
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
packet, typ, err = _recv_packet(self)
if not packet then
return nil, err
end
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
if typ == 'EOF' then
local warning_count, status_flags = _parse_eof_packet(packet)
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
--print('status flags: ', status_flags)
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
if band(status_flags, SERVER_MORE_RESULTS_EXISTS) ~= 0 then
return rows, 'again', cols
end
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
break
end
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
-- if typ ~= 'DATA' then
-- return nil, 'bad row packet type: ' .. typ
-- end
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
-- typ == 'DATA'
2021-04-29 21:24:55 +02:00
2021-08-18 19:36:08 +02:00
local row = _parse_row_data_packet(packet, cols, compact, to_array, null_value)
2021-04-30 08:59:49 +02:00
i = i + 1
rows[i] = row
end
2021-04-29 21:24:55 +02:00
2021-08-18 19:36:08 +02:00
self.state = 'ready'
2021-04-29 21:24:55 +02:00
2021-04-30 08:59:49 +02:00
return rows, nil, cols
2021-04-29 21:24:55 +02:00
end
2021-08-18 19:36:08 +02:00
function conn:query(query, opt)
2021-04-30 08:59:49 +02:00
local bytes, err, errcode = self:send_query(query)
if not bytes then return nil, err, errcode end
2021-08-18 19:36:08 +02:00
return self:read_result(opt)
2021-04-29 21:24:55 +02:00
end
local qmap = {
2021-04-30 08:59:49 +02:00
['\0' ] = '\\0',
['\b' ] = '\\b',
['\n' ] = '\\n',
['\r' ] = '\\r',
['\t' ] = '\\t',
['\26'] = '\\Z',
['\\' ] = '\\\\',
['\'' ] = '\\\'',
['\"' ] = '\\"',
2021-04-29 21:24:55 +02:00
}
2021-04-30 08:59:49 +02:00
function mysql.quote(s)
return s:gsub('[%z\b\n\r\t\26\\\'\"]', qmap)
2021-04-29 21:24:55 +02:00
end
2021-04-30 08:59:49 +02:00
return mysql