1 Star 0 Fork 345

wxxbard / swoole-src

forked from swoole / swoole-src 
加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
swoole_mysql.c 95.78 KB
一键复制 编辑 原始数据 按行查看 历史
twosee 提交于 2018-09-03 21:07 . Fix warning.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165
/*
+----------------------------------------------------------------------+
| Swoole |
+----------------------------------------------------------------------+
| Copyright (c) 2012-2015 The Swoole Group |
+----------------------------------------------------------------------+
| This source file is subject to version 2.0 of the Apache license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.apache.org/licenses/LICENSE-2.0.html |
| If you did not receive a copy of the Apache2.0 license and are unable|
| to obtain it through the world-wide-web, please send a note to |
| license@swoole.com so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Author: Tianfeng Han <mikan.tenny@gmail.com> |
+----------------------------------------------------------------------+
*/
#include "php_swoole.h"
#include "swoole_mysql.h"
// see mysqlnd 'L64' macro redefined
#undef L64
#include <ext/hash/php_hash.h>
#include <ext/hash/php_hash_sha.h>
#include <ext/standard/php_math.h>
#ifdef SW_MYSQL_RSA_SUPPORT
#include <openssl/rsa.h>
#include <openssl/pem.h>
#endif
static PHP_METHOD(swoole_mysql, __construct);
static PHP_METHOD(swoole_mysql, __destruct);
static PHP_METHOD(swoole_mysql, connect);
#ifdef SW_USE_MYSQLND
static PHP_METHOD(swoole_mysql, escape);
#endif
static PHP_METHOD(swoole_mysql, query);
static PHP_METHOD(swoole_mysql, begin);
static PHP_METHOD(swoole_mysql, commit);
static PHP_METHOD(swoole_mysql, rollback);
static PHP_METHOD(swoole_mysql, getState);
static PHP_METHOD(swoole_mysql, close);
static PHP_METHOD(swoole_mysql, on);
static zend_class_entry swoole_mysql_ce;
static zend_class_entry *swoole_mysql_class_entry_ptr;
static zend_class_entry swoole_mysql_exception_ce;
static zend_class_entry *swoole_mysql_exception_class_entry_ptr;
#define UTF8_MB4 "utf8mb4"
#define UTF8_MB3 "utf8"
typedef struct _mysql_charset
{
unsigned int nr;
const char *name;
const char *collation;
} mysql_charset;
static const mysql_charset swoole_mysql_charsets[] =
{
{ 1, "big5", "big5_chinese_ci" },
{ 3, "dec8", "dec8_swedish_ci" },
{ 4, "cp850", "cp850_general_ci" },
{ 6, "hp8", "hp8_english_ci" },
{ 7, "koi8r", "koi8r_general_ci" },
{ 8, "latin1", "latin1_swedish_ci" },
{ 5, "latin1", "latin1_german1_ci" },
{ 9, "latin2", "latin2_general_ci" },
{ 2, "latin2", "latin2_czech_cs" },
{ 10, "swe7", "swe7_swedish_ci" },
{ 11, "ascii", "ascii_general_ci" },
{ 12, "ujis", "ujis_japanese_ci" },
{ 13, "sjis", "sjis_japanese_ci" },
{ 16, "hebrew", "hebrew_general_ci" },
{ 17, "filename", "filename" },
{ 18, "tis620", "tis620_thai_ci" },
{ 19, "euckr", "euckr_korean_ci" },
{ 21, "latin2", "latin2_hungarian_ci" },
{ 27, "latin2", "latin2_croatian_ci" },
{ 22, "koi8u", "koi8u_general_ci" },
{ 24, "gb2312", "gb2312_chinese_ci" },
{ 25, "greek", "greek_general_ci" },
{ 26, "cp1250", "cp1250_general_ci" },
{ 28, "gbk", "gbk_chinese_ci" },
{ 30, "latin5", "latin5_turkish_ci" },
{ 31, "latin1", "latin1_german2_ci" },
{ 15, "latin1", "latin1_danish_ci" },
{ 32, "armscii8", "armscii8_general_ci" },
{ 33, UTF8_MB3, UTF8_MB3"_general_ci" },
{ 35, "ucs2", "ucs2_general_ci" },
{ 36, "cp866", "cp866_general_ci" },
{ 37, "keybcs2", "keybcs2_general_ci" },
{ 38, "macce", "macce_general_ci" },
{ 39, "macroman", "macroman_general_ci" },
{ 40, "cp852", "cp852_general_ci" },
{ 41, "latin7", "latin7_general_ci" },
{ 20, "latin7", "latin7_estonian_cs" },
{ 57, "cp1256", "cp1256_general_ci" },
{ 59, "cp1257", "cp1257_general_ci" },
{ 63, "binary", "binary" },
{ 97, "eucjpms", "eucjpms_japanese_ci" },
{ 29, "cp1257", "cp1257_lithuanian_ci" },
{ 31, "latin1", "latin1_german2_ci" },
{ 34, "cp1250", "cp1250_czech_cs" },
{ 42, "latin7", "latin7_general_cs" },
{ 43, "macce", "macce_bin" },
{ 44, "cp1250", "cp1250_croatian_ci" },
{ 45, UTF8_MB4, UTF8_MB4"_general_ci" },
{ 46, UTF8_MB4, UTF8_MB4"_bin" },
{ 47, "latin1", "latin1_bin" },
{ 48, "latin1", "latin1_general_ci" },
{ 49, "latin1", "latin1_general_cs" },
{ 51, "cp1251", "cp1251_general_ci" },
{ 14, "cp1251", "cp1251_bulgarian_ci" },
{ 23, "cp1251", "cp1251_ukrainian_ci" },
{ 50, "cp1251", "cp1251_bin" },
{ 52, "cp1251", "cp1251_general_cs" },
{ 53, "macroman", "macroman_bin" },
{ 54, "utf16", "utf16_general_ci" },
{ 55, "utf16", "utf16_bin" },
{ 56, "utf16le", "utf16le_general_ci" },
{ 58, "cp1257", "cp1257_bin" },
{ 60, "utf32", "utf32_general_ci" },
{ 61, "utf32", "utf32_bin" },
{ 62, "utf16le", "utf16le_bin" },
{ 64, "armscii8", "armscii8_bin" },
{ 65, "ascii", "ascii_bin" },
{ 66, "cp1250", "cp1250_bin" },
{ 67, "cp1256", "cp1256_bin" },
{ 68, "cp866", "cp866_bin" },
{ 69, "dec8", "dec8_bin" },
{ 70, "greek", "greek_bin" },
{ 71, "hebrew", "hebrew_bin" },
{ 72, "hp8", "hp8_bin" },
{ 73, "keybcs2", "keybcs2_bin" },
{ 74, "koi8r", "koi8r_bin" },
{ 75, "koi8u", "koi8u_bin" },
{ 77, "latin2", "latin2_bin" },
{ 78, "latin5", "latin5_bin" },
{ 79, "latin7", "latin7_bin" },
{ 80, "cp850", "cp850_bin" },
{ 81, "cp852", "cp852_bin" },
{ 82, "swe7", "swe7_bin" },
{ 83, UTF8_MB3, UTF8_MB3"_bin" },
{ 84, "big5", "big5_bin" },
{ 85, "euckr", "euckr_bin" },
{ 86, "gb2312", "gb2312_bin" },
{ 87, "gbk", "gbk_bin" },
{ 88, "sjis", "sjis_bin" },
{ 89, "tis620", "tis620_bin" },
{ 90, "ucs2", "ucs2_bin" },
{ 91, "ujis", "ujis_bin" },
{ 92, "geostd8", "geostd8_general_ci" },
{ 93, "geostd8", "geostd8_bin" },
{ 94, "latin1", "latin1_spanish_ci" },
{ 95, "cp932", "cp932_japanese_ci" },
{ 96, "cp932", "cp932_bin" },
{ 97, "eucjpms", "eucjpms_japanese_ci" },
{ 98, "eucjpms", "eucjpms_bin" },
{ 99, "cp1250", "cp1250_polish_ci" },
{ 128, "ucs2", "ucs2_unicode_ci" },
{ 129, "ucs2", "ucs2_icelandic_ci" },
{ 130, "ucs2", "ucs2_latvian_ci" },
{ 131, "ucs2", "ucs2_romanian_ci" },
{ 132, "ucs2", "ucs2_slovenian_ci" },
{ 133, "ucs2", "ucs2_polish_ci" },
{ 134, "ucs2", "ucs2_estonian_ci" },
{ 135, "ucs2", "ucs2_spanish_ci" },
{ 136, "ucs2", "ucs2_swedish_ci" },
{ 137, "ucs2", "ucs2_turkish_ci" },
{ 138, "ucs2", "ucs2_czech_ci" },
{ 139, "ucs2", "ucs2_danish_ci" },
{ 140, "ucs2", "ucs2_lithuanian_ci" },
{ 141, "ucs2", "ucs2_slovak_ci" },
{ 142, "ucs2", "ucs2_spanish2_ci" },
{ 143, "ucs2", "ucs2_roman_ci" },
{ 144, "ucs2", "ucs2_persian_ci" },
{ 145, "ucs2", "ucs2_esperanto_ci" },
{ 146, "ucs2", "ucs2_hungarian_ci" },
{ 147, "ucs2", "ucs2_sinhala_ci" },
{ 148, "ucs2", "ucs2_german2_ci" },
{ 149, "ucs2", "ucs2_croatian_ci" },
{ 150, "ucs2", "ucs2_unicode_520_ci" },
{ 151, "ucs2", "ucs2_vietnamese_ci" },
{ 160, "utf32", "utf32_unicode_ci" },
{ 161, "utf32", "utf32_icelandic_ci" },
{ 162, "utf32", "utf32_latvian_ci" },
{ 163, "utf32", "utf32_romanian_ci" },
{ 164, "utf32", "utf32_slovenian_ci" },
{ 165, "utf32", "utf32_polish_ci" },
{ 166, "utf32", "utf32_estonian_ci" },
{ 167, "utf32", "utf32_spanish_ci" },
{ 168, "utf32", "utf32_swedish_ci" },
{ 169, "utf32", "utf32_turkish_ci" },
{ 170, "utf32", "utf32_czech_ci" },
{ 171, "utf32", "utf32_danish_ci" },
{ 172, "utf32", "utf32_lithuanian_ci" },
{ 173, "utf32", "utf32_slovak_ci" },
{ 174, "utf32", "utf32_spanish2_ci" },
{ 175, "utf32", "utf32_roman_ci" },
{ 176, "utf32", "utf32_persian_ci" },
{ 177, "utf32", "utf32_esperanto_ci" },
{ 178, "utf32", "utf32_hungarian_ci" },
{ 179, "utf32", "utf32_sinhala_ci" },
{ 180, "utf32", "utf32_german2_ci" },
{ 181, "utf32", "utf32_croatian_ci" },
{ 182, "utf32", "utf32_unicode_520_ci" },
{ 183, "utf32", "utf32_vietnamese_ci" },
{ 192, UTF8_MB3, UTF8_MB3"_unicode_ci" },
{ 193, UTF8_MB3, UTF8_MB3"_icelandic_ci" },
{ 194, UTF8_MB3, UTF8_MB3"_latvian_ci" },
{ 195, UTF8_MB3, UTF8_MB3"_romanian_ci" },
{ 196, UTF8_MB3, UTF8_MB3"_slovenian_ci" },
{ 197, UTF8_MB3, UTF8_MB3"_polish_ci" },
{ 198, UTF8_MB3, UTF8_MB3"_estonian_ci" },
{ 199, UTF8_MB3, UTF8_MB3"_spanish_ci" },
{ 200, UTF8_MB3, UTF8_MB3"_swedish_ci" },
{ 201, UTF8_MB3, UTF8_MB3"_turkish_ci" },
{ 202, UTF8_MB3, UTF8_MB3"_czech_ci" },
{ 203, UTF8_MB3, UTF8_MB3"_danish_ci" },
{ 204, UTF8_MB3, UTF8_MB3"_lithuanian_ci" },
{ 205, UTF8_MB3, UTF8_MB3"_slovak_ci" },
{ 206, UTF8_MB3, UTF8_MB3"_spanish2_ci" },
{ 207, UTF8_MB3, UTF8_MB3"_roman_ci" },
{ 208, UTF8_MB3, UTF8_MB3"_persian_ci" },
{ 209, UTF8_MB3, UTF8_MB3"_esperanto_ci" },
{ 210, UTF8_MB3, UTF8_MB3"_hungarian_ci" },
{ 211, UTF8_MB3, UTF8_MB3"_sinhala_ci" },
{ 212, UTF8_MB3, UTF8_MB3"_german2_ci" },
{ 213, UTF8_MB3, UTF8_MB3"_croatian_ci" },
{ 214, UTF8_MB3, UTF8_MB3"_unicode_520_ci" },
{ 215, UTF8_MB3, UTF8_MB3"_vietnamese_ci" },
{ 224, UTF8_MB4, UTF8_MB4"_unicode_ci" },
{ 225, UTF8_MB4, UTF8_MB4"_icelandic_ci" },
{ 226, UTF8_MB4, UTF8_MB4"_latvian_ci" },
{ 227, UTF8_MB4, UTF8_MB4"_romanian_ci" },
{ 228, UTF8_MB4, UTF8_MB4"_slovenian_ci" },
{ 229, UTF8_MB4, UTF8_MB4"_polish_ci" },
{ 230, UTF8_MB4, UTF8_MB4"_estonian_ci" },
{ 231, UTF8_MB4, UTF8_MB4"_spanish_ci" },
{ 232, UTF8_MB4, UTF8_MB4"_swedish_ci" },
{ 233, UTF8_MB4, UTF8_MB4"_turkish_ci" },
{ 234, UTF8_MB4, UTF8_MB4"_czech_ci" },
{ 235, UTF8_MB4, UTF8_MB4"_danish_ci" },
{ 236, UTF8_MB4, UTF8_MB4"_lithuanian_ci" },
{ 237, UTF8_MB4, UTF8_MB4"_slovak_ci" },
{ 238, UTF8_MB4, UTF8_MB4"_spanish2_ci" },
{ 239, UTF8_MB4, UTF8_MB4"_roman_ci" },
{ 240, UTF8_MB4, UTF8_MB4"_persian_ci" },
{ 241, UTF8_MB4, UTF8_MB4"_esperanto_ci" },
{ 242, UTF8_MB4, UTF8_MB4"_hungarian_ci" },
{ 243, UTF8_MB4, UTF8_MB4"_sinhala_ci" },
{ 244, UTF8_MB4, UTF8_MB4"_german2_ci" },
{ 245, UTF8_MB4, UTF8_MB4"_croatian_ci" },
{ 246, UTF8_MB4, UTF8_MB4"_unicode_520_ci" },
{ 247, UTF8_MB4, UTF8_MB4"_vietnamese_ci" },
{ 248, "gb18030", "gb18030_chinese_ci" },
{ 249, "gb18030", "gb18030_bin" },
{ 254, UTF8_MB3, UTF8_MB3"_general_cs" },
{ 0, NULL, NULL},
};
ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_void, 0, 0, 0)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_mysql_on, 0, 0, 2)
ZEND_ARG_INFO(0, event_name)
ZEND_ARG_INFO(0, callback)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_mysql_connect, 0, 0, 2)
ZEND_ARG_ARRAY_INFO(0, server_config, 0)
ZEND_ARG_INFO(0, callback)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_mysql_begin, 0, 0, 1)
ZEND_ARG_INFO(0, callback)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_mysql_commit, 0, 0, 1)
ZEND_ARG_INFO(0, callback)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_mysql_rollback, 0, 0, 1)
ZEND_ARG_INFO(0, callback)
ZEND_END_ARG_INFO()
#ifdef SW_USE_MYSQLND
ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_mysql_escape, 0, 0, 1)
ZEND_ARG_INFO(0, string)
ZEND_ARG_INFO(0, flags)
ZEND_END_ARG_INFO()
#endif
ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_mysql_query, 0, 0, 2)
ZEND_ARG_INFO(0, sql)
ZEND_ARG_INFO(0, callback)
ZEND_END_ARG_INFO()
static const zend_function_entry swoole_mysql_methods[] =
{
PHP_ME(swoole_mysql, __construct, arginfo_swoole_void, ZEND_ACC_PUBLIC | ZEND_ACC_CTOR)
PHP_ME(swoole_mysql, __destruct, arginfo_swoole_void, ZEND_ACC_PUBLIC | ZEND_ACC_DTOR)
PHP_ME(swoole_mysql, connect, arginfo_swoole_mysql_connect, ZEND_ACC_PUBLIC)
PHP_ME(swoole_mysql, begin, arginfo_swoole_mysql_begin, ZEND_ACC_PUBLIC)
PHP_ME(swoole_mysql, commit, arginfo_swoole_mysql_commit, ZEND_ACC_PUBLIC)
PHP_ME(swoole_mysql, rollback, arginfo_swoole_mysql_rollback, ZEND_ACC_PUBLIC)
#ifdef SW_USE_MYSQLND
PHP_ME(swoole_mysql, escape, arginfo_swoole_mysql_escape, ZEND_ACC_PUBLIC)
#endif
PHP_ME(swoole_mysql, query, arginfo_swoole_mysql_query, ZEND_ACC_PUBLIC)
PHP_ME(swoole_mysql, close, arginfo_swoole_void, ZEND_ACC_PUBLIC)
PHP_ME(swoole_mysql, getState, arginfo_swoole_void, ZEND_ACC_PUBLIC)
PHP_ME(swoole_mysql, on, arginfo_swoole_mysql_on, ZEND_ACC_PUBLIC)
PHP_FE_END
};
static void mysql_client_free(mysql_client *client, zval* zobject);
static void mysql_columns_free(mysql_client *client);
static void mysql_client_free(mysql_client *client, zval* zobject)
{
if (client->cli->timer)
{
swTimer_del(&SwooleG.timer, client->cli->timer);
client->cli->timer = NULL;
}
//close the connection
client->cli->close(client->cli);
//release client object memory
swClient_free(client->cli);
efree(client->cli);
client->cli = NULL;
client->connected = 0;
}
static void mysql_columns_free(mysql_client *client)
{
int i;
for (i = 0; i < client->response.num_column; i++)
{
if (client->response.columns[i].buffer)
{
efree(client->response.columns[i].buffer);
client->response.columns[i].buffer = NULL;
}
}
efree(client->response.columns);
client->response.columns = NULL;
}
#ifdef SW_MYSQL_DEBUG
static void mysql_client_info(mysql_client *client);
static void mysql_column_info(mysql_field *field);
#endif
static void swoole_mysql_onTimeout(swTimer *timer, swTimer_node *tnode);
static int swoole_mysql_onRead(swReactor *reactor, swEvent *event);
static int swoole_mysql_onWrite(swReactor *reactor, swEvent *event);
static int swoole_mysql_onError(swReactor *reactor, swEvent *event);
static void swoole_mysql_onConnect(mysql_client *client TSRMLS_DC);
void swoole_mysql_init(int module_number TSRMLS_DC)
{
SWOOLE_INIT_CLASS_ENTRY(swoole_mysql_ce, "swoole_mysql", "Swoole\\MySQL", swoole_mysql_methods);
swoole_mysql_class_entry_ptr = zend_register_internal_class(&swoole_mysql_ce TSRMLS_CC);
SWOOLE_CLASS_ALIAS(swoole_mysql, "Swoole\\MySQL");
SWOOLE_INIT_CLASS_ENTRY(swoole_mysql_exception_ce, "swoole_mysql_exception", "Swoole\\MySQL\\Exception", NULL);
swoole_mysql_exception_class_entry_ptr = sw_zend_register_internal_class_ex(&swoole_mysql_exception_ce, zend_exception_get_default(TSRMLS_C), NULL TSRMLS_CC);
SWOOLE_CLASS_ALIAS(swoole_mysql_exception, "Swoole\\MySQL\\Exception");
zend_declare_property_null(swoole_mysql_class_entry_ptr, ZEND_STRL("serverInfo"), ZEND_ACC_PUBLIC TSRMLS_CC);
zend_declare_property_null(swoole_mysql_class_entry_ptr, ZEND_STRL("sock"), ZEND_ACC_PUBLIC TSRMLS_CC);
zend_declare_property_bool(swoole_mysql_class_entry_ptr, ZEND_STRL("connected"), 0, ZEND_ACC_PUBLIC TSRMLS_CC);
zend_declare_property_long(swoole_mysql_class_entry_ptr, ZEND_STRL("errno"), 0, ZEND_ACC_PUBLIC TSRMLS_CC);
zend_declare_property_long(swoole_mysql_class_entry_ptr, ZEND_STRL("connect_errno"), 0, ZEND_ACC_PUBLIC TSRMLS_CC);
zend_declare_property_null(swoole_mysql_class_entry_ptr, ZEND_STRL("error"), ZEND_ACC_PUBLIC TSRMLS_CC);
zend_declare_property_null(swoole_mysql_class_entry_ptr, ZEND_STRL("connect_error"), ZEND_ACC_PUBLIC TSRMLS_CC);
zend_declare_property_null(swoole_mysql_class_entry_ptr, ZEND_STRL("insert_id"), ZEND_ACC_PUBLIC TSRMLS_CC);
zend_declare_property_null(swoole_mysql_class_entry_ptr, ZEND_STRL("affected_rows"), ZEND_ACC_PUBLIC TSRMLS_CC);
/**
* event callback
*/
zend_declare_property_null(swoole_mysql_class_entry_ptr, ZEND_STRL("onConnect"), ZEND_ACC_PUBLIC TSRMLS_CC);
zend_declare_property_null(swoole_mysql_class_entry_ptr, ZEND_STRL("onClose"), ZEND_ACC_PUBLIC TSRMLS_CC);
zend_declare_class_constant_long(swoole_mysql_class_entry_ptr, SW_STRL("STATE_QUERY")-1, SW_MYSQL_STATE_QUERY TSRMLS_CC);
zend_declare_class_constant_long(swoole_mysql_class_entry_ptr, SW_STRL("STATE_READ_START")-1, SW_MYSQL_STATE_READ_START TSRMLS_CC);
zend_declare_class_constant_long(swoole_mysql_class_entry_ptr, SW_STRL("STATE_READ_FIELD ")-1, SW_MYSQL_STATE_READ_FIELD TSRMLS_CC);
zend_declare_class_constant_long(swoole_mysql_class_entry_ptr, SW_STRL("STATE_READ_ROW")-1, SW_MYSQL_STATE_READ_ROW TSRMLS_CC);
zend_declare_class_constant_long(swoole_mysql_class_entry_ptr, SW_STRL("STATE_READ_END")-1, SW_MYSQL_STATE_READ_END TSRMLS_CC);
zend_declare_class_constant_long(swoole_mysql_class_entry_ptr, SW_STRL("STATE_CLOSED")-1, SW_MYSQL_STATE_CLOSED TSRMLS_CC);
}
int mysql_request(swString *sql, swString *buffer)
{
bzero(buffer->str, 5);
//length
mysql_pack_length(sql->length + 1, buffer->str);
//command
buffer->str[4] = SW_MYSQL_COM_QUERY;
buffer->length = 5;
return swString_append(buffer, sql);
}
int mysql_prepare(swString *sql, swString *buffer)
{
bzero(buffer->str, 5);
//length
mysql_pack_length(sql->length + 1, buffer->str);
//command
buffer->str[4] = SW_MYSQL_COM_STMT_PREPARE;
buffer->length = 5;
return swString_append(buffer, sql);
}
int mysql_get_charset(char *name)
{
const mysql_charset *c = swoole_mysql_charsets;
while (c[0].nr != 0)
{
if (!strcasecmp(c->name, name))
{
return c->nr;
}
++c;
}
return -1;
}
int mysql_get_result(mysql_connector *connector, char *buf, int len)
{
char *tmp = buf;
int packet_length = mysql_uint3korr(tmp);
if (len < packet_length + 4)
{
return 0;
}
//int packet_number = tmp[3];
tmp += 4;
uint8_t opcode = *tmp;
tmp += 1;
//ERROR Packet
if (opcode == 0xff)
{
connector->error_code = *(uint16_t *) tmp;
connector->error_msg = tmp + 2;
connector->error_length = packet_length - 3;
return -1;
}
else
{
return 1;
}
}
static void php_swoole_sha256(const char *str, int _len, unsigned char *digest)
{
PHP_SHA256_CTX context;
PHP_SHA256Init(&context);
PHP_SHA256Update(&context, (unsigned char *) str, _len);
PHP_SHA256Final(digest, &context);
}
//sha256
static void mysql_sha2_password_with_nonce(char* ret, char* nonce, char* password, zend_size_t password_len)
{
// XOR(SHA256(password), SHA256(SHA256(SHA256(password)), nonce))
char hashed[32], double_hashed[32];
php_swoole_sha256(password, password_len, (unsigned char *) hashed);
php_swoole_sha256(hashed, 32, (unsigned char *) double_hashed);
char combined[32 + SW_MYSQL_NONCE_LENGTH]; //double-hashed + nonce
memcpy(combined, double_hashed, 32);
memcpy(combined + 32, nonce, SW_MYSQL_NONCE_LENGTH);
char xor_bytes[32];
php_swoole_sha256(combined, 32 + SW_MYSQL_NONCE_LENGTH, (unsigned char *) xor_bytes);
int i;
for (i = 0; i < 32; i++)
{
hashed[i] ^= xor_bytes[i];
}
memcpy(ret, hashed, 32);
}
/**
* Return: password length
*/
static int mysql_auth_encrypt_dispatch(char *buf, char *auth_plugin_name, char *password, zend_size_t password_len, char* nonce, int *next_state)
{
if (!auth_plugin_name || strcasecmp("mysql_native_password", auth_plugin_name) == 0)
{
// mysql_native_password is default
// auth-response
char hash_0[20];
bzero(hash_0, sizeof (hash_0));
php_swoole_sha1(password, password_len, (uchar *) hash_0);
char hash_1[20];
bzero(hash_1, sizeof (hash_1));
php_swoole_sha1(hash_0, sizeof (hash_0), (uchar *) hash_1);
char str[40];
memcpy(str, nonce, 20);
memcpy(str + 20, hash_1, 20);
char hash_2[20];
php_swoole_sha1(str, sizeof (str), (uchar *) hash_2);
char hash_3[20];
int *a = (int *) hash_2;
int *b = (int *) hash_0;
int *c = (int *) hash_3;
int i;
for (i = 0; i < 5; i++)
{
c[i] = a[i] ^ b[i];
}
memcpy(buf, hash_3, 20);
return 20;
}
else if (strcasecmp("caching_sha2_password", auth_plugin_name) == 0)
{
char hashed[32];
mysql_sha2_password_with_nonce(
(char *) hashed,
(char *) nonce,
password,
password_len
);
// copy hashed data to connector buf
memcpy(buf, (char *) hashed, 32);
*next_state = SW_MYSQL_HANDSHAKE_WAIT_SIGNATURE;
return 32;
}
else
{
// unknown
swWarn("Unknown auth plugin: %s", auth_plugin_name);
return 0;
}
}
/**
1 [0a] protocol version
string[NUL] server version
4 connection id
string[8] auth-plugin-data-part-1
1 [00] filler
2 capability flags (lower 2 bytes)
if more data in the packet:
1 character set
2 status flags
2 capability flags (upper 2 bytes)
if capabilities & CLIENT_PLUGIN_AUTH {
1 length of auth-plugin-data
} else {
1 [00]
}
string[10] reserved (all [00])
if capabilities & CLIENT_SECURE_CONNECTION {
string[$len] auth-plugin-data-part-2 ($len=MAX(13, length of auth-plugin-data - 8))
if capabilities & CLIENT_PLUGIN_AUTH {
string[NUL] auth-plugin name
}
*/
int mysql_handshake(mysql_connector *connector, char *buf, int len)
{
char *tmp = buf;
int next_state = SW_MYSQL_HANDSHAKE_WAIT_RESULT; // ret is the next handshake state
/**
* handshake request
*/
mysql_handshake_request request;
bzero(&request, sizeof(request));
request.packet_length = mysql_uint3korr(tmp);
//continue to wait for data
if (len < request.packet_length + 4)
{
return 0;
}
request.packet_number = tmp[3];
tmp += 4;
request.protocol_version = *tmp;
tmp += 1;
//ERROR Packet
if (request.protocol_version == 0xff)
{
connector->error_code = *(uint16_t *) tmp;
connector->error_msg = tmp + 2;
connector->error_length = request.packet_length - 3;
return -1;
}
//1 [0a] protocol version
request.server_version = tmp;
tmp += (strlen(request.server_version) + 1);
//4 connection id
request.connection_id = *((int *) tmp);
tmp += 4;
//string[8] auth-plugin-data-part-1
memcpy(request.auth_plugin_data, tmp, 8);
tmp += 8;
//1 [00] filler
request.filler = *tmp;
tmp += 1;
//2 capability flags (lower 2 bytes)
memcpy(((char *) (&request.capability_flags)), tmp, 2);
tmp += 2;
if (tmp - tmp < len)
{
//1 character set
request.character_set = *tmp;
tmp += 1;
//2 status flags
memcpy(&request.status_flags, tmp, 2);
tmp += 2;
//2 capability flags (upper 2 bytes)
memcpy(((char *) (&request.capability_flags) + 2), tmp, 2);
tmp += 2;
request.l_auth_plugin_data = *tmp;
tmp += 1;
memcpy(&request.reserved, tmp, sizeof(request.reserved));
tmp += sizeof(request.reserved);
if (request.capability_flags & SW_MYSQL_CLIENT_SECURE_CONNECTION)
{
int len = MAX(13, request.l_auth_plugin_data - 8);
memcpy(request.auth_plugin_data + 8, tmp, len);
#ifdef SW_MYSQL_RSA_SUPPORT
memcpy(connector->auth_plugin_data, request.auth_plugin_data, SW_MYSQL_NONCE_LENGTH);
#endif
tmp += len;
}
if (request.capability_flags & SW_MYSQL_CLIENT_PLUGIN_AUTH)
{
request.auth_plugin_name = tmp;
request.l_auth_plugin_name = MIN(strlen(tmp), len - (tmp - buf));
swTraceLog(SW_TRACE_MYSQL_CLIENT, "use %s auth plugin", request.auth_plugin_name);
}
}
int value;
tmp = connector->buf + 4;
//capability flags, CLIENT_PROTOCOL_41 always set
value = SW_MYSQL_CLIENT_LONG_PASSWORD | SW_MYSQL_CLIENT_PROTOCOL_41 | SW_MYSQL_CLIENT_SECURE_CONNECTION
| SW_MYSQL_CLIENT_CONNECT_WITH_DB | SW_MYSQL_CLIENT_PLUGIN_AUTH | SW_MYSQL_CLIENT_MULTI_RESULTS;
memcpy(tmp, &value, sizeof(value));
tmp += 4;
//max-packet size
value = 300;
memcpy(tmp, &value, sizeof(value));
tmp += 4;
//use the server character_set when the character_set is not set.
if (connector->character_set == 0)
{
connector->character_set = request.character_set;
}
//character set
*tmp = connector->character_set;
tmp += 1;
//string[23] reserved (all [0])
tmp += 23;
//string[NUL] username
memcpy(tmp, connector->user, connector->user_len);
tmp[connector->user_len] = '\0';
tmp += (connector->user_len + 1);
if (connector->password_len > 0)
{
int length = 0;
length = mysql_auth_encrypt_dispatch(
tmp + 1,
request.auth_plugin_name,
connector->password,
connector->password_len,
request.auth_plugin_data,
&next_state
);
*tmp = length;
tmp += length + 1;
}
else
{
*tmp = 0;
tmp++;
}
//string[NUL] database
memcpy(tmp, connector->database, connector->database_len);
tmp[connector->database_len] = '\0';
tmp += (connector->database_len + 1);
//string[NUL] auth plugin name
memcpy(tmp, request.auth_plugin_name, request.l_auth_plugin_name);
tmp[request.l_auth_plugin_name] = '\0';
tmp += (request.l_auth_plugin_name + 1);
connector->packet_length = tmp - connector->buf - 4;
mysql_pack_length(connector->packet_length, connector->buf);
connector->buf[3] = 1;
return next_state;
}
// we may need it one day but now
// we can reply the every auth plugin requirement on the first handshake
int mysql_auth_switch(mysql_connector *connector, char *buf, int len)
{
char *tmp = buf;
if ((uint8_t) tmp[4] != 0xfe)
{
// out of the order package
return SW_ERR;
}
int next_state = SW_MYSQL_HANDSHAKE_WAIT_RESULT;
int packet_length = mysql_uint3korr(tmp);
//continue to wait for data
if (len < packet_length + 4)
{
return SW_AGAIN;
}
int packet_number = tmp[3];
tmp += 4;
// type
tmp += 1;
// clear
connector->packet_length = 0;
memset(connector->buf, 0, 512);
// string[NUL] plugin name
char auth_plugin_name[32];
int auth_plugin_name_len = 0;
int i;
for (i = 0; i < packet_length; i++)
{
auth_plugin_name[auth_plugin_name_len] = tmp[auth_plugin_name_len];
auth_plugin_name_len++;
if (tmp[auth_plugin_name_len] == 0x00)
{
break;
}
}
auth_plugin_name[auth_plugin_name_len] = '\0';
swTraceLog(SW_TRACE_MYSQL_CLIENT, "auth switch plugin name=%s", auth_plugin_name);
tmp += auth_plugin_name_len + 1; // name + 0x00
// if auth switch is triggered, password can't be empty
// string auth plugin data
char auth_plugin_data[20];
memcpy((char *)auth_plugin_data, tmp, 20);
// create auth switch response package
connector->packet_length += mysql_auth_encrypt_dispatch(
(char *) (connector->buf + 4),
auth_plugin_name,
connector->password,
connector->password_len,
auth_plugin_data,
&next_state
);
// 3 for package length
mysql_pack_length(connector->packet_length, connector->buf);
// 1 package num
connector->buf[3] = packet_number + 1;
return next_state;
}
int mysql_parse_auth_signature(swString *buffer, mysql_connector *connector)
{
char *tmp = buffer->str;
int packet_length = mysql_uint3korr(tmp);
//continue to wait for data
if (buffer->length < packet_length + 4)
{
return SW_AGAIN;
}
int packet_number = tmp[3];
tmp += 4;
// signature
if ((uint8_t) tmp[0] != SW_MYSQL_AUTH_SIGNATURE)
{
return SW_MYSQL_AUTH_SIGNATURE_ERROR;
}
// remaining length
buffer->offset = 4 + packet_length;
swTraceLog(SW_TRACE_MYSQL_CLIENT, "before signature remaining=%ju", (uintmax_t) (buffer->length - buffer->offset));
if ((uint8_t)tmp[1] == SW_MYSQL_AUTH_SIGNATURE_FULL_AUTH_REQUIRED)
{
// create RSA prepared response
connector->packet_length = 1;
memset(connector->buf, 0, 512);
// 3 for package length
mysql_pack_length(connector->packet_length, connector->buf);
// 1 packet number
connector->buf[3] = packet_number + 1;
// as I am OK
connector->buf[4] = SW_MYSQL_AUTH_SIGNATURE_RSA_PREPARED;
}
// signature value
return tmp[1];
}
#ifdef SW_MYSQL_RSA_SUPPORT
// Caching sha2 authentication. Public key request and send encrypted password
// http://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::AuthSwitchResponse
int mysql_parse_rsa(mysql_connector *connector, char *buf, int len)
{
// clear
connector->packet_length = 0;
memset(connector->buf, 0, 512);
char *tmp = buf;
int packet_length = mysql_uint3korr(tmp);
//continue to wait for data
if (len < packet_length + 4)
{
return SW_AGAIN;
}
int packet_number = tmp[3];
tmp += 4;
int rsa_public_key_length = packet_length;
while (tmp[0] != 0x2d)
{
tmp++; // ltrim
rsa_public_key_length--;
}
char rsa_public_key[rsa_public_key_length + 1]; //rsa + '\0'
memcpy((char *)rsa_public_key, tmp, rsa_public_key_length);
rsa_public_key[rsa_public_key_length] = '\0';
swTraceLog(SW_TRACE_MYSQL_CLIENT, "rsa-length=%d;\nrsa-key=[%.*s]", rsa_public_key_length, rsa_public_key_length, rsa_public_key);
int password_len = connector->password_len + 1;
unsigned char password[password_len];
// copy to stack
memcpy((char *)password, connector->password, password_len);
// add NUL terminator to password
password[password_len - 1] = '\0';
// XOR the password bytes with the challenge
int i;
for (i = 0; i < password_len; i++)
{
password[i] ^= connector->auth_plugin_data[i % SW_MYSQL_NONCE_LENGTH];
}
// prepare RSA public key
BIO *bio = NULL;
RSA *public_rsa = NULL;
if (unlikely((bio = BIO_new_mem_buf((void *)rsa_public_key, -1)) == NULL))
{
swError("BIO_new_mem_buf publicKey error!");
return SW_ERR;
}
// PEM_read_bio_RSA_PUBKEY
ERR_clear_error();
if (unlikely((public_rsa = PEM_read_bio_RSA_PUBKEY(bio, NULL, NULL, NULL)) == NULL))
{
ERR_load_crypto_strings();
char err_buf[512];
ERR_error_string_n(ERR_get_error(), err_buf, sizeof(err_buf));
swError("[PEM_read_bio_RSA_PUBKEY ERROR]: %s", err_buf);
return SW_ERR;
}
BIO_free_all(bio);
// encrypt with RSA public key
int rsa_len = RSA_size(public_rsa);
unsigned char encrypt_msg[rsa_len];
// RSA_public_encrypt
ERR_clear_error();
int flen = rsa_len - 42;
flen = password_len > flen ? flen : password_len;
swDebug("rsa_len=%d", rsa_len);
if (unlikely(RSA_public_encrypt(flen, (const unsigned char *)password, (unsigned char *)encrypt_msg, public_rsa, RSA_PKCS1_OAEP_PADDING) < 0))
{
ERR_load_crypto_strings();
char err_buf[512];
ERR_error_string_n(ERR_get_error(), err_buf, sizeof(err_buf));
swError("[RSA_public_encrypt ERROR]: %s", err_buf);
return SW_ERR;
}
RSA_free(public_rsa);
memcpy((char *)connector->buf + 4, (char *)encrypt_msg, rsa_len); // copy rsa to buf
connector->packet_length = rsa_len;
// 3 for package length
mysql_pack_length(connector->packet_length, connector->buf);
// 1 packet number
connector->buf[3] = packet_number + 1;
return SW_OK;
}
#endif
static int mysql_parse_prepare_result(mysql_client *client, char *buf, size_t n_buf)
{
if (n_buf < 11)
{
return SW_ERR;
}
mysql_statement *stmt = emalloc(sizeof(mysql_statement));
stmt->id = mysql_uint4korr(buf);
buf += 4;
stmt->field_count = mysql_uint2korr(buf);
buf += 2;
stmt->unreaded_param_count = stmt->param_count = mysql_uint2korr(buf);
buf += 2;
//skip 1 byte
buf += 1;
stmt->warning_count = mysql_uint2korr(buf);
stmt->result = NULL;
stmt->buffer = NULL;
client->statement = stmt;
stmt->client = client;
swTraceLog(SW_TRACE_MYSQL_CLIENT, "id=%d, field_count=%d, param_count=%d, warning_count=%d.", stmt->id, stmt->field_count, stmt->param_count,
stmt->warning_count);
return 11;
}
static int mysql_decode_row(mysql_client *client, char *buf, int packet_len)
{
int read_n = 0, i;
int tmp_len;
ulong_t len;
char nul;
mysql_row row;
char value_buffer[32];
bzero(&row, sizeof(row));
char *error;
//unused
//char mem;
zval *result_array = client->response.result_array;
zval *row_array = NULL;
SW_ALLOC_INIT_ZVAL(row_array);
array_init(row_array);
swTraceLog(SW_TRACE_MYSQL_CLIENT, "mysql_decode_row begin, num_column=%ld, packet_len=%d.", client->response.num_column, packet_len);
mysql_field *field = NULL;
for (i = 0; i < client->response.num_column; i++)
{
tmp_len = mysql_length_coded_binary(&buf[read_n], &len, &nul, packet_len - read_n);
if (tmp_len == -1)
{
return -SW_MYSQL_ERR_BAD_LCB;
}
read_n += tmp_len;
if (read_n + len > packet_len)
{
return -SW_MYSQL_ERR_LEN_OVER_BUFFER;
}
field = &client->response.columns[i];
swTraceLog(SW_TRACE_MYSQL_CLIENT, "n=%d, field_name=%s, name_length=%d", i, field->name, field->name_length);
if (nul == 1)
{
add_assoc_null(row_array, field->name);
continue;
}
swTraceLog(SW_TRACE_MYSQL_CLIENT, "value: name=%s, type=%d, value=%s, len=%ld", field->name, field->type, swoole_strndup(buf + read_n, len), len);
switch (field->type)
{
case SW_MYSQL_TYPE_NULL:
add_assoc_null(row_array, field->name);
break;
/* String */
case SW_MYSQL_TYPE_TINY_BLOB:
case SW_MYSQL_TYPE_MEDIUM_BLOB:
case SW_MYSQL_TYPE_LONG_BLOB:
case SW_MYSQL_TYPE_BLOB:
case SW_MYSQL_TYPE_DECIMAL:
case SW_MYSQL_TYPE_NEWDECIMAL:
case SW_MYSQL_TYPE_BIT:
case SW_MYSQL_TYPE_STRING:
case SW_MYSQL_TYPE_VAR_STRING:
case SW_MYSQL_TYPE_VARCHAR:
case SW_MYSQL_TYPE_NEWDATE:
/* Date Time */
case SW_MYSQL_TYPE_TIME:
case SW_MYSQL_TYPE_YEAR:
case SW_MYSQL_TYPE_TIMESTAMP:
case SW_MYSQL_TYPE_DATETIME:
case SW_MYSQL_TYPE_DATE:
case SW_MYSQL_TYPE_JSON:
sw_add_assoc_stringl(row_array, field->name, buf + read_n, len, 1);
break;
/* Integer */
case SW_MYSQL_TYPE_TINY:
case SW_MYSQL_TYPE_SHORT:
case SW_MYSQL_TYPE_INT24:
case SW_MYSQL_TYPE_LONG:
if(client->connector.strict_type)
{
memcpy(value_buffer, buf + read_n, len);
value_buffer[len] = 0;
if (field->flags & SW_MYSQL_UNSIGNED_FLAG)
{
row.uint = strtoul(value_buffer, &error, 10);
if (*error != '\0')
{
return -SW_MYSQL_ERR_CONVLONG;
}
add_assoc_long(row_array, field->name, row.uint);
}
else
{
row.sint = strtol(value_buffer, &error, 10);
if (*error != '\0')
{
return -SW_MYSQL_ERR_CONVLONG;
}
add_assoc_long(row_array, field->name, row.sint);
}
}
else
{
sw_add_assoc_stringl(row_array, field->name, buf + read_n, len, 1);
}
break;
case SW_MYSQL_TYPE_LONGLONG:
if(client->connector.strict_type) {
memcpy(value_buffer, buf + read_n, len);
value_buffer[len] = 0;
if (field->flags & SW_MYSQL_UNSIGNED_FLAG)
{
row.ubigint = strtoull(value_buffer, &error, 10);
if (*error != '\0') {
return -SW_MYSQL_ERR_CONVLONGLONG;
}
if (unlikely(row.ubigint > ZEND_LONG_MAX))
{
goto _longlongstring;
}
add_assoc_long(row_array, field->name, row.ubigint);
}
else
{
row.sbigint = strtoll(value_buffer, &error, 10);
if (*error != '\0') {
return -SW_MYSQL_ERR_CONVLONGLONG;
}
add_assoc_long(row_array, field->name, row.sbigint);
}
}
else
{
_longlongstring:
sw_add_assoc_stringl(row_array, field->name, buf + read_n, len, 1);
}
break;
case SW_MYSQL_TYPE_FLOAT:
if(client->connector.strict_type) {
memcpy(value_buffer, buf + read_n, len);
value_buffer[len] = 0;
row.mdouble = strtod(value_buffer, &error);
if (*error != '\0') {
return -SW_MYSQL_ERR_CONVFLOAT;
}
add_assoc_double(row_array, field->name, row.mdouble);
}
else
{
sw_add_assoc_stringl(row_array, field->name, buf + read_n, len, 1);
}
break;
case SW_MYSQL_TYPE_DOUBLE:
if(client->connector.strict_type) {
memcpy(value_buffer, buf + read_n, len);
value_buffer[len] = 0;
row.mdouble = strtod(value_buffer, &error);
if (*error != '\0') {
return -SW_MYSQL_ERR_CONVDOUBLE;
}
add_assoc_double(row_array, field->name, row.mdouble);
}
else
{
sw_add_assoc_stringl(row_array, field->name, buf + read_n, len, 1);
}
break;
default:
swWarn("unknown field type[%d].", field->type);
return -1;
}
read_n += len;
}
add_next_index_zval(result_array, row_array);
if (row_array)
{
efree(row_array);
}
return read_n;
}
#define DATETIME_MAX_SIZE 20
static int mysql_decode_datetime(char *buf, char *result)
{
uint16_t y = 0;
uint8_t M = 0, d = 0, h = 0, m = 0, s = 0, n;
n = *(uint8_t *) (buf);
if (n != 0)
{
y = *(uint16_t *) (buf + 1);
M = *(uint8_t *) (buf + 3);
d = *(uint8_t *) (buf + 4);
if (n > 4)
{
h = *(uint8_t *) (buf + 5);
m = *(uint8_t *) (buf + 6);
s = *(uint8_t *) (buf + 7);
}
}
snprintf(result, DATETIME_MAX_SIZE, "%04d-%02d-%02d %02d:%02d:%02d", y, M, d, h, m, s);
swTraceLog(SW_TRACE_MYSQL_CLIENT, "n=%d", n);
return n;
}
static int mysql_decode_time(char *buf, char *result)
{
uint8_t h = 0, m = 0, s = 0;
uint8_t n = *(uint8_t *) (buf);
if (n != 0)
{
h = *(uint8_t *) (buf + 6);
m = *(uint8_t *) (buf + 7);
s = *(uint8_t *) (buf + 8);
}
snprintf(result, DATETIME_MAX_SIZE, "%02d:%02d:%02d", h, m, s);
swTraceLog(SW_TRACE_MYSQL_CLIENT, "n=%d", n);
return n;
}
static int mysql_decode_date(char *buf, char *result)
{
uint8_t M = 0, d = 0, n;
uint16_t y = 0;
n = *(uint8_t *) (buf);
if (n != 0)
{
y = *(uint16_t *) (buf + 1);
M = *(uint8_t *) (buf + 3);
d = *(uint8_t *) (buf + 4);
}
snprintf(result, DATETIME_MAX_SIZE, "%04d-%02d-%02d", y, M, d);
swTraceLog(SW_TRACE_MYSQL_CLIENT, "n=%d", n);
return n;
}
static void mysql_decode_year(char *buf, char *result)
{
uint16_t y = *(uint16_t *) (buf);
snprintf(result, DATETIME_MAX_SIZE, "%04d", y);
}
static int mysql_decode_row_prepare(mysql_client *client, char *buf, int packet_len)
{
int read_n = 0, i;
int tmp_len;
ulong_t len = 0;
char nul;
unsigned int null_count = ((client->response.num_column + 9) / 8) + 1;
buf += null_count;
packet_len -= null_count;
swTraceLog(SW_TRACE_MYSQL_CLIENT, "null_count=%d", null_count);
char datetime_buffer[DATETIME_MAX_SIZE];
mysql_row row;
zval *result_array = client->response.result_array;
zval *row_array = NULL;
SW_ALLOC_INIT_ZVAL(row_array);
array_init(row_array);
swTraceLog(SW_TRACE_MYSQL_CLIENT, "mysql_decode_row begin, num_column=%ld, packet_len=%d.", client->response.num_column, packet_len);
mysql_field *field = NULL;
for (i = 0; i < client->response.num_column; i++)
{
field = &client->response.columns[i];
/* to check Null-Bitmap @see https://dev.mysql.com/doc/internals/en/null-bitmap.html */
if (((buf - null_count + 1)[((i + 2) / 8)] & (0x01 << ((i + 2) % 8))) != 0)
{
swTraceLog(SW_TRACE_MYSQL_CLIENT, "value: %s is null ,flag2", field->name);
add_assoc_null(row_array, field->name);
continue;
}
swTraceLog(SW_TRACE_MYSQL_CLIENT, "value: name=%s, type=%d, len=%lu", field->name, field->type, field->length);
switch (field->type)
{
/* Date Time */
case SW_MYSQL_TYPE_TIME:
len = mysql_decode_time(buf + read_n, datetime_buffer) + 1;
sw_add_assoc_stringl(row_array, field->name, datetime_buffer, 8, 1);
swTraceLog(SW_TRACE_MYSQL_CLIENT, "%s=%s", field->name, datetime_buffer);
break;
case SW_MYSQL_TYPE_YEAR:
mysql_decode_year(buf + read_n, datetime_buffer);
sw_add_assoc_stringl(row_array, field->name, datetime_buffer, 4, 1);
len = 2;
swTraceLog(SW_TRACE_MYSQL_CLIENT, "%s=%s", field->name, datetime_buffer);
break;
case SW_MYSQL_TYPE_DATE:
len = mysql_decode_date(buf + read_n, datetime_buffer) + 1;
sw_add_assoc_stringl(row_array, field->name, datetime_buffer, 10, 1);
swTraceLog(SW_TRACE_MYSQL_CLIENT, "%s=%s", field->name, datetime_buffer);
break;
case SW_MYSQL_TYPE_TIMESTAMP:
case SW_MYSQL_TYPE_DATETIME:
len = mysql_decode_datetime(buf + read_n, datetime_buffer) + 1;
sw_add_assoc_stringl(row_array, field->name, datetime_buffer, 19, 1);
swTraceLog(SW_TRACE_MYSQL_CLIENT, "%s=%s", field->name, datetime_buffer);
break;
case SW_MYSQL_TYPE_NULL:
add_assoc_null(row_array, field->name);
break;
/* String */
case SW_MYSQL_TYPE_TINY_BLOB:
case SW_MYSQL_TYPE_MEDIUM_BLOB:
case SW_MYSQL_TYPE_LONG_BLOB:
case SW_MYSQL_TYPE_BLOB:
case SW_MYSQL_TYPE_DECIMAL:
case SW_MYSQL_TYPE_NEWDECIMAL:
case SW_MYSQL_TYPE_BIT:
case SW_MYSQL_TYPE_JSON:
case SW_MYSQL_TYPE_STRING:
case SW_MYSQL_TYPE_VAR_STRING:
case SW_MYSQL_TYPE_VARCHAR:
case SW_MYSQL_TYPE_NEWDATE:
tmp_len = mysql_length_coded_binary(&buf[read_n], &len, &nul, packet_len - read_n);
if (tmp_len == -1)
{
return -SW_MYSQL_ERR_BAD_LCB;
}
read_n += tmp_len;
sw_add_assoc_stringl(row_array, field->name, buf + read_n, len, 1);
swTraceLog(SW_TRACE_MYSQL_CLIENT, "%s=%s", field->name, swoole_strndup(buf + read_n, len));
break;
/* Integer */
case SW_MYSQL_TYPE_TINY:
if (field->flags & SW_MYSQL_UNSIGNED_FLAG)
{
row.utiny = *(uint8_t *) (buf + read_n);
add_assoc_long(row_array, field->name, row.utiny);
len = sizeof(row.utiny);
swTraceLog(SW_TRACE_MYSQL_CLIENT, "%s=%u", field->name, row.utiny);
}
else
{
row.stiny = *(int8_t *) (buf + read_n);
add_assoc_long(row_array, field->name, row.stiny);
len = sizeof(row.stiny);
swTraceLog(SW_TRACE_MYSQL_CLIENT, "%s=%d", field->name, row.stiny);
}
break;
case SW_MYSQL_TYPE_SHORT:
if (field->flags & SW_MYSQL_UNSIGNED_FLAG)
{
row.small = *(uint16_t *) (buf + read_n);
add_assoc_long(row_array, field->name, row.small);
len = sizeof(row.small);
swTraceLog(SW_TRACE_MYSQL_CLIENT, "%s=%u", field->name, row.small);
}
else
{
row.ssmall = *(int16_t *) (buf + read_n);
add_assoc_long(row_array, field->name, row.ssmall);
len = sizeof(row.ssmall);
swTraceLog(SW_TRACE_MYSQL_CLIENT, "%s=%d", field->name, row.ssmall);
}
break;
case SW_MYSQL_TYPE_INT24:
case SW_MYSQL_TYPE_LONG:
if (field->flags & SW_MYSQL_UNSIGNED_FLAG)
{
row.uint = *(uint32_t *) (buf + read_n);
add_assoc_long(row_array, field->name, row.uint);
len = sizeof(row.uint);
swTraceLog(SW_TRACE_MYSQL_CLIENT, "%s=%u", field->name, row.uint);
}
else
{
row.sint = *(int32_t *) (buf + read_n);
add_assoc_long(row_array, field->name, row.sint);
len = sizeof(row.sint);
swTraceLog(SW_TRACE_MYSQL_CLIENT, "%s=%d", field->name, row.sint);
}
break;
case SW_MYSQL_TYPE_LONGLONG:
if (field->flags & SW_MYSQL_UNSIGNED_FLAG)
{
row.ubigint = *(uint64_t *) (buf + read_n);
add_assoc_ulong_safe(row_array, field->name, row.ubigint);
len = sizeof(row.ubigint);
swTraceLog(SW_TRACE_MYSQL_CLIENT, "%s=%llu", field->name, row.ubigint);
}
else
{
row.sbigint = *(int64_t *) (buf + read_n);
add_assoc_long(row_array, field->name, row.sbigint);
len = sizeof(row.sbigint);
swTraceLog(SW_TRACE_MYSQL_CLIENT, "%s=%lld", field->name, row.sbigint);
}
break;
case SW_MYSQL_TYPE_FLOAT:
row.mfloat = *(float *) (buf + read_n);
swTraceLog(SW_TRACE_MYSQL_CLIENT, "%s=%.7f", field->name, row.mfloat);
row.mdouble = _php_math_round(row.mfloat, 5, PHP_ROUND_HALF_DOWN);
add_assoc_double(row_array, field->name, row.mdouble);
len = sizeof(row.mfloat);
break;
case SW_MYSQL_TYPE_DOUBLE:
row.mdouble = *(double *) (buf + read_n);
swTraceLog(SW_TRACE_MYSQL_CLIENT, "%s=%.16f", field->name, row.mdouble);
add_assoc_double(row_array, field->name, row.mdouble);
len = sizeof(row.mdouble);
break;
default:
swWarn("unknown field type[%d].", field->type);
return -1;
}
read_n += len;
}
add_next_index_zval(result_array, row_array);
if (row_array)
{
efree(row_array);
}
return read_n + null_count;
}
static sw_inline int mysql_read_eof(mysql_client *client, char *buffer, int n_buf)
{
//EOF, length (3byte) + id(1byte) + 0xFE + warning(2byte) + status(2byte)
if (n_buf < 9)
{
client->response.wait_recv = 1;
return SW_ERR;
}
client->response.packet_length = mysql_uint3korr(buffer);
client->response.packet_number = buffer[3];
//not EOF packet
uint8_t eof = buffer[4];
if (eof != 0xfe)
{
return SW_ERR;
}
client->response.warnings = mysql_uint2korr(buffer + 5);
client->response.status_code = mysql_uint2korr(buffer + 7);
MYSQL_RESPONSE_BUFFER->offset += client->response.packet_length + 4;
return SW_OK;
}
static sw_inline int mysql_read_params(mysql_client *client)
{
while (1)
{
swString *buffer = MYSQL_RESPONSE_BUFFER;
char *t_buffer = buffer->str + buffer->offset;
uint32_t n_buf = buffer->length - buffer->offset;
swTraceLog(SW_TRACE_MYSQL_CLIENT, "n_buf=%d, length=%d.", n_buf, client->response.packet_length);
if (n_buf < 4)
{
swTraceLog(SW_TRACE_MYSQL_CLIENT, "read eof [1]");
return SW_ERR;
}
if (client->statement->unreaded_param_count > 0)
{
//no enough data
if (n_buf - 4 < client->response.packet_length)
{
swTraceLog(SW_TRACE_MYSQL_CLIENT, "read eof 234234.");
return SW_ERR;
}
// Read and ignore parameter field. Sentence from MySQL source:
// skip parameters data: we don't support it yet
client->response.packet_length = mysql_uint3korr(t_buffer);
client->response.packet_number = t_buffer[3];
buffer->offset += (client->response.packet_length + 4);
client->statement->unreaded_param_count--;
swTraceLog(SW_TRACE_MYSQL_CLIENT, "read param, count=%d.", client->statement->unreaded_param_count);
continue;
}
else
{
swTraceLog(SW_TRACE_MYSQL_CLIENT, "read eof [2]");
if (mysql_read_eof(client, t_buffer, n_buf) == 0)
{
return SW_OK;
}
else
{
return SW_ERR;
}
}
}
}
static sw_inline int mysql_read_rows(mysql_client *client)
{
swString *buffer = MYSQL_RESPONSE_BUFFER;
char *t_buffer = buffer->str + buffer->offset;
uint32_t n_buf = buffer->length - buffer->offset;
int ret;
swTraceLog(SW_TRACE_MYSQL_CLIENT, "n_buf=%d", n_buf);
//RecordSet parse
while (n_buf > 0)
{
if (n_buf < 4)
{
client->response.wait_recv = 1;
return SW_ERR;
}
//RecordSet end
else if (mysql_read_eof(client, t_buffer, n_buf) == SW_OK)
{
if (client->response.columns)
{
mysql_columns_free(client);
}
return SW_OK;
}
client->response.packet_length = mysql_uint3korr(t_buffer);
client->response.packet_number = t_buffer[3];
t_buffer += 4;
n_buf -= 4;
swTraceLog(SW_TRACE_MYSQL_CLIENT, "record size=%d", client->response.packet_length);
//no enough data
if (n_buf < client->response.packet_length)
{
client->response.wait_recv = 1;
return SW_ERR;
}
if (client->cmd == SW_MYSQL_COM_STMT_EXECUTE)
{
ret = mysql_decode_row_prepare(client, t_buffer, client->response.packet_length);
}
else
{
//decode
ret = mysql_decode_row(client, t_buffer, client->response.packet_length);
}
if (ret < 0)
{
break;
}
//next row
client->response.num_row++;
t_buffer += client->response.packet_length;
n_buf -= client->response.packet_length;
buffer->offset += client->response.packet_length + 4;
}
return SW_ERR;
}
static int mysql_decode_field(char *buf, int len, mysql_field *col)
{
int i;
ulong_t size;
char nul;
char *wh;
int tmp_len;
/**
* string buffer
*/
char *_buffer = (char*) emalloc(len);
if (!_buffer)
{
return -SW_MYSQL_ERR_BAD_LCB;
}
col->buffer = _buffer;
wh = buf;
i = 0;
tmp_len = mysql_length_coded_binary(&buf[i], &size, &nul, len - i);
if (tmp_len == -1)
{
return -SW_MYSQL_ERR_BAD_LCB;
}
i += tmp_len;
if (i + size > len)
{
return -SW_MYSQL_ERR_LEN_OVER_BUFFER;
}
col->catalog_length = size;
col->catalog = _buffer;
_buffer += (size + 1);
memcpy(col->catalog, &buf[i], size);
col->catalog[size] = '\0';
wh += size + 1;
i += size;
/* n (Length Coded String) db */
tmp_len = mysql_length_coded_binary(&buf[i], &size, &nul, len - i);
if (tmp_len == -1)
{
return -SW_MYSQL_ERR_BAD_LCB;
}
i += tmp_len;
if (i + size > len)
{
return -SW_MYSQL_ERR_LEN_OVER_BUFFER;
}
col->db_length = size;
col->db = _buffer;
_buffer += (size + 1);
memcpy(col->db, &buf[i], size);
col->db[size] = '\0';
wh += size + 1;
i += size;
/* n (Length Coded String) table */
tmp_len = mysql_length_coded_binary(&buf[i], &size, &nul, len - i);
if (tmp_len == -1)
{
return -SW_MYSQL_ERR_BAD_LCB;
}
i += tmp_len;
if (i + size > len)
{
return -SW_MYSQL_ERR_LEN_OVER_BUFFER;
}
col->table_length = size;
col->table = _buffer;
_buffer += (size + 1);
memcpy(col->table, &buf[i], size);
col->table[size] = '\0';
wh += size + 1;
i += size;
/* n (Length Coded String) org_table */
tmp_len = mysql_length_coded_binary(&buf[i], &size, &nul, len - i);
if (tmp_len == -1)
{
return -SW_MYSQL_ERR_BAD_LCB;
}
i += tmp_len;
if (i + size > len)
{
return -SW_MYSQL_ERR_LEN_OVER_BUFFER;
}
col->org_table_length = size;
col->org_table = _buffer;
_buffer += (size + 1);
memcpy(col->org_table, &buf[i], size);
col->org_table[size] = '\0';
wh += size + 1;
i += size;
/* n (Length Coded String) name */
tmp_len = mysql_length_coded_binary(&buf[i], &size, &nul, len - i);
if (tmp_len == -1)
{
return -SW_MYSQL_ERR_BAD_LCB;
}
i += tmp_len;
if (i + size > len)
{
return -SW_MYSQL_ERR_LEN_OVER_BUFFER;
}
col->name_length = size;
col->name = _buffer;
_buffer += (size + 1);
memcpy(col->name, &buf[i], size);
col->name[size] = '\0';
wh += size + 1;
i += size;
/* n (Length Coded String) org_name */
tmp_len = mysql_length_coded_binary(&buf[i], &size, &nul, len - i);
if (tmp_len == -1)
{
return -SW_MYSQL_ERR_BAD_LCB;
}
i += tmp_len;
if (i + size > len)
{
return -SW_MYSQL_ERR_LEN_OVER_BUFFER;
}
col->org_name_length = size;
col->org_name = _buffer;
_buffer += (size + 1);
memcpy(col->org_name, &buf[i], size);
col->org_name[size] = '\0';
wh += size + 1;
i += size;
/* check len */
if (i + 13 > len)
{
return -SW_MYSQL_ERR_LEN_OVER_BUFFER;
}
/* (filler) */
i += 1;
/* charset */
col->charsetnr = mysql_uint2korr(&buf[i]);
i += 2;
/* length */
col->length = mysql_uint4korr(&buf[i]);
i += 4;
/* type */
col->type = (enum mysql_field_types) (uchar)buf[i];
i += 1;
/* flags */
col->flags = mysql_uint2korr(&buf[i]);
i += 2;
/* decimals */
col->decimals = buf[i];
i += 1;
/* filler */
i += 2;
/* default - a priori facultatif */
if (len - i > 0)
{
tmp_len = mysql_length_coded_binary(&buf[i], &size, &nul, len - i);
if (tmp_len == -1)
{
return -SW_MYSQL_ERR_BAD_LCB;
}
i += tmp_len;
if (i + size > len)
{
return -SW_MYSQL_ERR_LEN_OVER_BUFFER;
}
col->def_length = size;
col->def = _buffer;
//_buffer += (size + 1);
memcpy(col->def, &buf[i], size);
col->def[size] = '\0';
wh += size + 1;
i += size;
}
else
{
col->def = NULL;
col->def_length = 0;
}
/* set write pointer */
return wh - buf;
}
static int mysql_read_columns(mysql_client *client)
{
swString *buffer = MYSQL_RESPONSE_BUFFER;
char *t_buffer = buffer->str + buffer->offset;
uint32_t n_buf = buffer->length - buffer->offset;
int ret;
for (; client->response.index_column < client->response.num_column; client->response.index_column++)
{
swTraceLog(SW_TRACE_MYSQL_CLIENT, "index_index_column=%ld, n_buf=%d.", client->response.index_column, n_buf);
if (n_buf < 4)
{
return SW_ERR;
}
client->response.packet_length = mysql_uint3korr(t_buffer);
//no enough data
if (n_buf - 4 < client->response.packet_length)
{
return SW_ERR;
}
client->response.packet_number = t_buffer[3];
t_buffer += 4;
n_buf -= 4;
ret = mysql_decode_field(t_buffer, client->response.packet_length, &client->response.columns[client->response.index_column]);
if (ret > 0)
{
t_buffer += client->response.packet_length;
n_buf -= client->response.packet_length;
buffer->offset += (client->response.packet_length + 4);
}
else
{
swWarn("mysql_decode_field failed, code=%d.", ret);
break;
}
}
if (mysql_read_eof(client, t_buffer, n_buf) < 0)
{
return SW_ERR;
}
t_buffer += 9;
n_buf -= 9;
if (client->cmd != SW_MYSQL_COM_STMT_PREPARE)
{
zval *result_array = client->response.result_array;
if (!result_array)
{
SW_ALLOC_INIT_ZVAL(result_array);
array_init(result_array);
client->response.result_array = result_array;
}
}
buffer->offset += t_buffer - (buffer->str + buffer->offset);
return SW_OK;
}
// this function is used to check if multi responses has received over.
int mysql_is_over(mysql_client *client)
{
swString *buffer = MYSQL_RESPONSE_BUFFER;
char *p;
if (client->check_offset == buffer->length)
{
// have already check all of the data
goto again;
}
size_t n_buf = buffer->length - client->check_offset; // remaining buffer size
uint32_t temp;
while (1)
{
p = buffer->str + client->check_offset; // where to start checking now
if (unlikely(buffer->length - buffer->offset < 5))
{
break;
}
temp = mysql_uint3korr(p); //package length
// add header
p += 4;
n_buf -= 4;
if (unlikely(n_buf < temp)) //package is incomplete
{
break;
}
else
{
client->check_offset += 4;
}
client->check_offset += temp; // add package length
if (client->check_offset >= buffer->length) // if false: more packages exist, skip the current one
{
switch ((uint8_t) p[0])
{
case 0xfe: // eof
{
// +type +warning
p += 3;
swDebug("meet eof and flag=%d", mysql_uint2korr(p));
goto check_flag;
}
case 0x00: // ok
{
// if (temp < 7)
// {
// break;
// }
ulong_t val = 0;
char nul;
int retcode;
int t_nbuf = n_buf;
//+type
p++;
t_nbuf--;
retcode = mysql_lcb_ll(p, &val, &nul, t_nbuf); //affecr rows
t_nbuf -= retcode;
p += retcode;
retcode = mysql_lcb_ll(p, &val, &nul, t_nbuf); //insert id
t_nbuf -= retcode;
p += retcode;
check_flag:
if ((mysql_uint2korr(p) & SW_MYSQL_SERVER_MORE_RESULTS_EXISTS) == 0)
{
over:
client->response.wait_recv = 0;
client->check_offset = 0;
return SW_OK;
}
break;
}
case 0xff: // response type = error
{
goto over;
}
}
}
n_buf -= temp;
if (n_buf == 0)
{
break;
}
}
again:
client->response.wait_recv = 2;
return SW_AGAIN;
}
int mysql_response(mysql_client *client)
{
swString *buffer = MYSQL_RESPONSE_BUFFER;
char *p = buffer->str + buffer->offset;
int ret;
char nul;
size_t n_buf = buffer->length - buffer->offset;
while (n_buf > 0)
{
swTraceLog(SW_TRACE_MYSQL_CLIENT, "client->state=%d, n_buf=%zu.", client->state, n_buf);
switch (client->state)
{
case SW_MYSQL_STATE_READ_START:
if (buffer->length - buffer->offset < 5)
{
client->response.wait_recv = 1;
return SW_ERR;
}
client->response.packet_length = mysql_uint3korr(p);
client->response.packet_number = p[3];
p += 4;
n_buf -= 4;
if (n_buf < client->response.packet_length)
{
client->response.wait_recv = 1;
return SW_ERR;
}
client->response.response_type = p[0];
p ++;
n_buf --;
/* error */
if (client->response.response_type == 0xff)
{
client->response.error_code = mysql_uint2korr(p);
/* status flag 1byte (#), skip.. */
memcpy(client->response.status_msg, p + 3, 5);
client->response.server_msg = p + 8;
/**
* int<1> header [ff] header of the ERR packet
* int<2> error_code error-code
* if capabilities & CLIENT_PROTOCOL_41 {
* string[1] sql_state_marker # marker of the SQL State
* string[5] sql_state SQL State
* }
*/
client->response.l_server_msg = client->response.packet_length - 9;
client->state = SW_MYSQL_STATE_READ_END;
return SW_OK;
}
/* eof */
else if (client->response.response_type == 0xfe)
{
client->response.warnings = mysql_uint2korr(p);
client->response.status_code = mysql_uint2korr(p + 2);
client->state = SW_MYSQL_STATE_READ_END;
return SW_OK;
}
/* ok */
else if (client->response.response_type == 0)
{
if (client->cmd == SW_MYSQL_COM_STMT_PREPARE)
{
ret = mysql_parse_prepare_result(client, p, n_buf);
if (ret < 0)
{
return SW_ERR;
}
else
{
p += ret;
n_buf -= ret;
buffer->offset += (5 + ret);
client->response.num_column = client->statement->field_count;
if (client->response.num_column > 0)
{
client->response.columns = ecalloc(client->response.num_column, sizeof(mysql_field));
}
if (client->statement->param_count > 0)
{
client->state = SW_MYSQL_STATE_READ_PARAM;
}
else
{
client->state = SW_MYSQL_STATE_READ_FIELD;
}
break;
}
}
/* affected rows */
ret = mysql_length_coded_binary(p, &client->response.affected_rows, &nul, n_buf);
n_buf -= ret;
p += ret;
/* insert id */
ret = mysql_length_coded_binary(p, &client->response.insert_id, &nul, n_buf);
n_buf -= ret;
p += ret;
/* server status */
client->response.status_code = mysql_uint2korr(p);
n_buf -= 2;
p += 2;
/* server warnings */
client->response.warnings = mysql_uint2korr(p);
client->state = SW_MYSQL_STATE_READ_END;
return SW_OK;
}
/* result set */
else
{
//Protocol::LengthEncodedInteger
ret = mysql_length_coded_binary(p - 1, &client->response.num_column, &nul, n_buf + 1);
if (ret < 0)
{
return SW_ERR;
}
buffer->offset += (4 + ret);
// easy to the safe side: but under what circumstances would num_column will be 0 in result set?
if (client->response.num_column > 0)
{
client->response.columns = ecalloc(client->response.num_column, sizeof(mysql_field));
}
client->state = SW_MYSQL_STATE_READ_FIELD;
break;
}
case SW_MYSQL_STATE_READ_FIELD:
if (mysql_read_columns(client) < 0)
{
return SW_ERR;
}
else
{
if (client->cmd == SW_MYSQL_COM_STMT_PREPARE)
{
mysql_columns_free(client);
return SW_OK;
}
client->state = SW_MYSQL_STATE_READ_ROW;
break;
}
case SW_MYSQL_STATE_READ_ROW:
if (mysql_read_rows(client) < 0)
{
return SW_ERR;
}
else
{
client->state = SW_MYSQL_STATE_READ_END;
return SW_OK;
}
case SW_MYSQL_STATE_READ_PARAM:
if (mysql_read_params(client) < 0)
{
return SW_ERR;
}
else if (client->statement->field_count > 0)
{
client->state = SW_MYSQL_STATE_READ_FIELD;
continue;
}
else
{
mysql_columns_free(client);
return SW_OK;
}
default:
return SW_ERR;
}
}
return SW_OK;
}
int mysql_query(zval *zobject, mysql_client *client, swString *sql, zval *callback TSRMLS_DC)
{
if (!client->cli)
{
SwooleG.error = SW_ERROR_CLIENT_NO_CONNECTION;
swoole_php_fatal_error(E_WARNING, "mysql connection#%d is closed.", client->fd);
return SW_ERR;
}
if (!client->connected)
{
SwooleG.error = SW_ERROR_CLIENT_NO_CONNECTION;
swoole_php_error(E_WARNING, "mysql client is not connected to server.");
return SW_ERR;
}
if (client->state != SW_MYSQL_STATE_QUERY)
{
swoole_php_fatal_error(E_WARNING, "mysql client is waiting response, cannot send new sql query.");
return SW_ERR;
}
if (callback != NULL)
{
sw_zval_add_ref(&callback);
client->callback = sw_zval_dup(callback);
}
client->cmd = SW_MYSQL_COM_QUERY;
swString_clear(mysql_request_buffer);
if (mysql_request(sql, mysql_request_buffer) < 0)
{
return SW_ERR;
}
//send query
if (SwooleG.main_reactor->write(SwooleG.main_reactor, client->fd, mysql_request_buffer->str, mysql_request_buffer->length) < 0)
{
//connection is closed
if (swConnection_error(errno) == SW_CLOSE)
{
zend_update_property_bool(swoole_mysql_class_entry_ptr, zobject, ZEND_STRL("connected"), 0 TSRMLS_CC);
zend_update_property_long(swoole_mysql_class_entry_ptr, zobject, ZEND_STRL("errno"), 2013 TSRMLS_CC);
zend_update_property_string(swoole_mysql_class_entry_ptr, zobject, ZEND_STRL("error"), "Lost connection to MySQL server during query" TSRMLS_CC);
}
return SW_ERR;
}
else
{
client->state = SW_MYSQL_STATE_READ_START;
return SW_OK;
}
}
#ifdef SW_MYSQL_DEBUG
void mysql_client_info(mysql_client *client)
{
printf("\n"SW_START_LINE"\nmysql_client\nbuffer->offset=%ld\nbuffer->length=%ld\nstatus=%d\n"
"packet_length=%d\npacket_number=%d\n"
"insert_id=%d\naffected_rows=%d\n"
"warnings=%d\n"SW_END_LINE, client->buffer->offset, client->buffer->length, client->response.status_code,
client->response.packet_length, client->response.packet_number,
client->response.insert_id, client->response.affected_rows,
client->response.warnings);
int i;
if (client->response.num_column)
{
for (i = 0; i < client->response.num_column; i++)
{
mysql_column_info(&client->response.columns[i]);
}
}
}
void mysql_column_info(mysql_field *field)
{
printf("\n"SW_START_LINE"\nname=%s, table=%s, db=%s\n"
"name_length=%d, table_length=%d, db_length=%d\n"
"catalog=%s, default_value=%s\n"
"length=%ld, type=%d\n"SW_END_LINE,
field->name, field->table, field->db,
field->name_length, field->table_length, field->db_length,
field->catalog, field->def,
field->length, field->type
);
}
#endif
static PHP_METHOD(swoole_mysql, __construct)
{
mysql_client *client = emalloc(sizeof(mysql_client));
bzero(client, sizeof(mysql_client));
swoole_set_object(getThis(), client);
}
static PHP_METHOD(swoole_mysql, connect)
{
zval *server_info;
zval *callback;
char buf[2048];
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "az", &server_info, &callback) == FAILURE)
{
RETURN_FALSE;
}
mysql_client *client = swoole_get_object(getThis());
if (client->cli)
{
swoole_php_error(E_WARNING, "The mysql client is already connected server.");
RETURN_FALSE;
}
php_swoole_array_separate(server_info);
HashTable *_ht = Z_ARRVAL_P(server_info);
zval *value;
mysql_connector *connector = &client->connector;
if (php_swoole_array_get_value(_ht, "host", value))
{
convert_to_string(value);
connector->host = Z_STRVAL_P(value);
connector->host_len = Z_STRLEN_P(value);
}
else
{
zend_throw_exception(swoole_mysql_exception_class_entry_ptr, "HOST parameter is required.", 11 TSRMLS_CC);
RETURN_FALSE;
}
if (php_swoole_array_get_value(_ht, "port", value))
{
convert_to_long(value);
connector->port = Z_LVAL_P(value);
}
else
{
connector->port = SW_MYSQL_DEFAULT_PORT;
}
if (php_swoole_array_get_value(_ht, "user", value))
{
convert_to_string(value);
connector->user = Z_STRVAL_P(value);
connector->user_len = Z_STRLEN_P(value);
}
else
{
zend_throw_exception(swoole_mysql_exception_class_entry_ptr, "USER parameter is required.", 11 TSRMLS_CC);
RETURN_FALSE;
}
if (php_swoole_array_get_value(_ht, "password", value))
{
convert_to_string(value);
connector->password = Z_STRVAL_P(value);
connector->password_len = Z_STRLEN_P(value);
}
else
{
zend_throw_exception(swoole_mysql_exception_class_entry_ptr, "PASSWORD parameter is required.", 11 TSRMLS_CC);
RETURN_FALSE;
}
if (php_swoole_array_get_value(_ht, "database", value))
{
convert_to_string(value);
connector->database = Z_STRVAL_P(value);
connector->database_len = Z_STRLEN_P(value);
}
else
{
zend_throw_exception(swoole_mysql_exception_class_entry_ptr, "DATABASE parameter is required.", 11 TSRMLS_CC);
RETURN_FALSE;
}
if (php_swoole_array_get_value(_ht, "timeout", value))
{
convert_to_double(value);
connector->timeout = Z_DVAL_P(value);
}
else
{
connector->timeout = SW_MYSQL_CONNECT_TIMEOUT;
}
if (php_swoole_array_get_value(_ht, "charset", value))
{
convert_to_string(value);
connector->character_set = mysql_get_charset(Z_STRVAL_P(value));
if (connector->character_set < 0)
{
snprintf(buf, sizeof(buf), "unknown charset [%s].", Z_STRVAL_P(value));
zend_throw_exception(swoole_mysql_exception_class_entry_ptr, buf, 11 TSRMLS_CC);
RETURN_FALSE;
}
}
//use the server default charset.
else
{
connector->character_set = 0;
}
if (php_swoole_array_get_value(_ht, "strict_type", value))
{
convert_to_boolean(value);
connector->strict_type = Z_BVAL_P(value);
}
if (php_swoole_array_get_value(_ht, "fetch_mode", value))
{
convert_to_boolean(value);
connector->fetch_mode = Z_BVAL_P(value);
}
swClient *cli = emalloc(sizeof(swClient));
int type = SW_SOCK_TCP;
if (strncasecmp(connector->host, ZEND_STRL("unix:/")) == 0)
{
connector->host = connector->host + 5;
connector->host_len = connector->host_len - 5;
type = SW_SOCK_UNIX_STREAM;
}
else if (strchr(connector->host, ':'))
{
type = SW_SOCK_TCP6;
}
php_swoole_check_reactor();
if (!swReactor_handle_isset(SwooleG.main_reactor, PHP_SWOOLE_FD_MYSQL))
{
SwooleG.main_reactor->setHandle(SwooleG.main_reactor, PHP_SWOOLE_FD_MYSQL | SW_EVENT_READ, swoole_mysql_onRead);
SwooleG.main_reactor->setHandle(SwooleG.main_reactor, PHP_SWOOLE_FD_MYSQL | SW_EVENT_WRITE, swoole_mysql_onWrite);
SwooleG.main_reactor->setHandle(SwooleG.main_reactor, PHP_SWOOLE_FD_MYSQL | SW_EVENT_ERROR, swoole_mysql_onError);
}
//create socket
if (swClient_create(cli, type, 0) < 0)
{
zend_throw_exception(swoole_mysql_exception_class_entry_ptr, "swClient_create failed.", 1 TSRMLS_CC);
RETURN_FALSE;
}
//tcp nodelay
if (type != SW_SOCK_UNIX_STREAM)
{
int tcp_nodelay = 1;
if (setsockopt(cli->socket->fd, IPPROTO_TCP, TCP_NODELAY, (const void *) &tcp_nodelay, sizeof(int)) == -1)
{
swoole_php_sys_error(E_WARNING, "setsockopt(%d, IPPROTO_TCP, TCP_NODELAY) failed.", cli->socket->fd);
}
}
//connect to mysql server
int ret = cli->connect(cli, connector->host, connector->port, connector->timeout, 1);
if ((ret < 0 && errno == EINPROGRESS) || ret == 0)
{
if (connector->timeout > 0)
{
php_swoole_check_timer((int) (connector->timeout * 1000));
cli->timer = SwooleG.timer.add(&SwooleG.timer, (int) (connector->timeout * 1000), 0, client, swoole_mysql_onTimeout);
cli->timeout = connector->timeout;
}
if (SwooleG.main_reactor->add(SwooleG.main_reactor, cli->socket->fd, PHP_SWOOLE_FD_MYSQL | SW_EVENT_WRITE) < 0)
{
RETURN_FALSE;
}
}
else
{
snprintf(buf, sizeof(buf), "connect to mysql server[%s:%ld] failed.", connector->host, connector->port);
zend_throw_exception(swoole_mysql_exception_class_entry_ptr, buf, 2 TSRMLS_CC);
RETURN_FALSE;
}
zend_update_property(swoole_mysql_class_entry_ptr, getThis(), ZEND_STRL("onConnect"), callback TSRMLS_CC);
zend_update_property(swoole_mysql_class_entry_ptr, getThis(), ZEND_STRL("serverInfo"), server_info TSRMLS_CC);
zend_update_property_long(swoole_mysql_class_entry_ptr, getThis(), ZEND_STRL("sock"), cli->socket->fd TSRMLS_CC);
client->buffer = swString_new(SW_BUFFER_SIZE_BIG);
client->fd = cli->socket->fd;
client->object = getThis();
client->cli = cli;
sw_copy_to_stack(client->object, client->_object);
sw_zval_add_ref(&client->object);
sw_zval_ptr_dtor(&server_info);
swConnection *_socket = swReactor_get(SwooleG.main_reactor, cli->socket->fd);
_socket->object = client;
_socket->active = 0;
RETURN_TRUE;
}
static PHP_METHOD(swoole_mysql, query)
{
zval *callback;
swString sql;
bzero(&sql, sizeof(sql));
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz", &sql.str, &sql.length, &callback) == FAILURE)
{
return;
}
if (!php_swoole_is_callable(callback TSRMLS_CC))
{
RETURN_FALSE;
}
if (sql.length <= 0)
{
swoole_php_fatal_error(E_WARNING, "Query is empty.");
RETURN_FALSE;
}
mysql_client *client = swoole_get_object(getThis());
if (!client)
{
swoole_php_fatal_error(E_WARNING, "object is not instanceof swoole_mysql.");
RETURN_FALSE;
}
SW_CHECK_RETURN(mysql_query(getThis(), client, &sql, callback TSRMLS_CC));
}
static PHP_METHOD(swoole_mysql, begin)
{
zval *callback;
if (zend_parse_parameters(ZEND_NUM_ARGS()TSRMLS_CC, "z", &callback) == FAILURE)
{
return;
}
if (!php_swoole_is_callable(callback TSRMLS_CC))
{
RETURN_FALSE;
}
mysql_client *client = swoole_get_object(getThis());
if (!client)
{
swoole_php_fatal_error(E_WARNING, "object is not instanceof swoole_mysql.");
RETURN_FALSE;
}
if (client->transaction)
{
zend_throw_exception(swoole_mysql_exception_class_entry_ptr, "There is already an active transaction.", 21 TSRMLS_CC);
RETURN_FALSE;
}
swString sql;
bzero(&sql, sizeof(sql));
swString_append_ptr(&sql, ZEND_STRL("START TRANSACTION"));
if (mysql_query(getThis(), client, &sql, callback TSRMLS_CC) < 0)
{
RETURN_FALSE;
}
else
{
client->transaction = 1;
RETURN_TRUE;
}
}
static PHP_METHOD(swoole_mysql, commit)
{
zval *callback;
if (zend_parse_parameters(ZEND_NUM_ARGS()TSRMLS_CC, "z", &callback) == FAILURE)
{
return;
}
if (!php_swoole_is_callable(callback TSRMLS_CC))
{
RETURN_FALSE;
}
mysql_client *client = swoole_get_object(getThis());
if (!client)
{
swoole_php_fatal_error(E_WARNING, "object is not instanceof swoole_mysql.");
RETURN_FALSE;
}
if (!client->transaction)
{
zend_throw_exception(swoole_mysql_exception_class_entry_ptr, "There is no active transaction.", 22 TSRMLS_CC);
RETURN_FALSE;
}
swString sql;
bzero(&sql, sizeof(sql));
swString_append_ptr(&sql, ZEND_STRL("COMMIT"));
if (mysql_query(getThis(), client, &sql, callback TSRMLS_CC) < 0)
{
RETURN_FALSE;
}
else
{
client->transaction = 0;
RETURN_TRUE;
}
}
static PHP_METHOD(swoole_mysql, rollback)
{
zval *callback;
if (zend_parse_parameters(ZEND_NUM_ARGS()TSRMLS_CC, "z", &callback) == FAILURE)
{
return;
}
if (!php_swoole_is_callable(callback TSRMLS_CC))
{
RETURN_FALSE;
}
mysql_client *client = swoole_get_object(getThis());
if (!client)
{
swoole_php_fatal_error(E_WARNING, "object is not instanceof swoole_mysql.");
RETURN_FALSE;
}
if (!client->transaction)
{
zend_throw_exception(swoole_mysql_exception_class_entry_ptr, "There is no active transaction.", 22 TSRMLS_CC);
RETURN_FALSE;
}
swString sql;
bzero(&sql, sizeof(sql));
swString_append_ptr(&sql, ZEND_STRL("ROLLBACK"));
if (mysql_query(getThis(), client, &sql, callback TSRMLS_CC) < 0)
{
RETURN_FALSE;
}
else
{
client->transaction = 0;
RETURN_TRUE;
}
}
static PHP_METHOD(swoole_mysql, __destruct)
{
SW_PREVENT_USER_DESTRUCT;
mysql_client *client = swoole_get_object(getThis());
if (!client)
{
return;
}
if (client->state != SW_MYSQL_STATE_CLOSED && client->cli)
{
zval *retval = NULL;
zval *zobject = getThis();
client->cli->destroyed = 1;
sw_zend_call_method_with_0_params(&zobject, swoole_mysql_class_entry_ptr, NULL, "close", &retval);
if (retval)
{
sw_zval_ptr_dtor(&retval);
}
}
//release buffer memory
if (client->buffer)
{
swString_free(client->buffer);
}
efree(client);
swoole_set_object(getThis(), NULL);
}
static PHP_METHOD(swoole_mysql, close)
{
mysql_client *client = swoole_get_object(getThis());
if (!client)
{
swoole_php_fatal_error(E_WARNING, "object is not instanceof swoole_mysql.");
RETURN_FALSE;
}
if (!client->cli)
{
RETURN_FALSE;
}
if (client->cli->socket->closing)
{
swoole_error_log(SW_LOG_NOTICE, SW_ERROR_SESSION_CLOSING, "The mysql connection[%d] is closing.", client->fd);
RETURN_FALSE;
}
zend_update_property_bool(swoole_mysql_class_entry_ptr, getThis(), ZEND_STRL("connected"), 0 TSRMLS_CC);
SwooleG.main_reactor->del(SwooleG.main_reactor, client->fd);
swConnection *socket = swReactor_get(SwooleG.main_reactor, client->fd);
bzero(socket, sizeof(swConnection));
socket->removed = 1;
zend_bool is_destroyed = client->cli->destroyed;
zval *retval = NULL;
zval **args[1];
zval *object = getThis();
if (client->onClose)
{
client->cli->socket->closing = 1;
args[0] = &object;
if (sw_call_user_function_ex(EG(function_table), NULL, client->onClose, &retval, 1, args, 0, NULL TSRMLS_CC) != SUCCESS)
{
swoole_php_fatal_error(E_WARNING, "swoole_mysql onClose callback error.");
}
if (EG(exception))
{
zend_exception_error(EG(exception), E_ERROR TSRMLS_CC);
}
if (retval)
{
sw_zval_ptr_dtor(&retval);
}
}
mysql_client_free(client, getThis());
if (!is_destroyed)
{
sw_zval_ptr_dtor(&object);
}
}
static PHP_METHOD(swoole_mysql, on)
{
char *name;
zend_size_t len;
zval *cb;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz", &name, &len, &cb) == FAILURE)
{
return;
}
mysql_client *client = swoole_get_object(getThis());
if (!client)
{
swoole_php_fatal_error(E_WARNING, "object is not instanceof swoole_mysql.");
RETURN_FALSE;
}
if (strncasecmp("close", name, len) == 0)
{
zend_update_property(swoole_mysql_class_entry_ptr, getThis(), ZEND_STRL("onClose"), cb TSRMLS_CC);
client->onClose = sw_zend_read_property(swoole_mysql_class_entry_ptr, getThis(), ZEND_STRL("onClose"), 0 TSRMLS_CC);
sw_copy_to_stack(client->onClose, client->_onClose);
}
else
{
swoole_php_error(E_WARNING, "Unknown event type[%s]", name);
RETURN_FALSE;
}
RETURN_TRUE;
}
static PHP_METHOD(swoole_mysql, getState)
{
mysql_client *client = swoole_get_object(getThis());
if (!client)
{
swoole_php_fatal_error(E_WARNING, "object is not instanceof swoole_mysql.");
RETURN_FALSE;
}
RETURN_LONG(client->state);
}
static void swoole_mysql_onTimeout(swTimer *timer, swTimer_node *tnode)
{
mysql_client *client = tnode->data;
client->connector.error_code = ETIMEDOUT;
client->connector.error_msg = strerror(client->connector.error_code);
client->connector.error_length = strlen(client->connector.error_msg);
swoole_mysql_onConnect(client TSRMLS_CC);
}
static int swoole_mysql_onError(swReactor *reactor, swEvent *event)
{
swClient *cli = event->socket->object;
if (cli && cli->socket && cli->socket->active)
{
mysql_client *client = event->socket->object;
if (!client)
{
close(event->fd);
return SW_ERR;
}
zval *retval = NULL;
zval *zobject = client->object;
sw_zend_call_method_with_0_params(&zobject, swoole_mysql_class_entry_ptr, NULL, "close", &retval);
if (retval)
{
sw_zval_ptr_dtor(&retval);
}
return SW_OK;
}
else
{
return swoole_mysql_onWrite(reactor, event);
}
}
static void swoole_mysql_onConnect(mysql_client *client TSRMLS_DC)
{
zval *zobject = client->object;
zval *callback = sw_zend_read_property(swoole_mysql_class_entry_ptr, zobject, ZEND_STRL("onConnect"), 0 TSRMLS_CC);
zval *retval = NULL;
zval *result;
zval **args[2];
SW_MAKE_STD_ZVAL(result);
if (client->cli->timer)
{
swTimer_del(&SwooleG.timer, client->cli->timer);
client->cli->timer = NULL;
}
if (client->connector.error_code > 0)
{
zend_update_property_stringl(swoole_mysql_class_entry_ptr, zobject, ZEND_STRL("connect_error"), client->connector.error_msg, client->connector.error_length TSRMLS_CC);
zend_update_property_long(swoole_mysql_class_entry_ptr, zobject, ZEND_STRL("connect_errno"), client->connector.error_code TSRMLS_CC);
ZVAL_BOOL(result, 0);
}
else
{
zend_update_property_bool(swoole_mysql_class_entry_ptr, zobject, ZEND_STRL("connected"), 1 TSRMLS_CC);
ZVAL_BOOL(result, 1);
client->connected = 1;
}
args[0] = &zobject;
args[1] = &result;
if (sw_call_user_function_ex(EG(function_table), NULL, callback, &retval, 2, args, 0, NULL TSRMLS_CC) != SUCCESS)
{
swoole_php_fatal_error(E_WARNING, "swoole_mysql onConnect handler error.");
}
if (EG(exception))
{
zend_exception_error(EG(exception), E_ERROR TSRMLS_CC);
}
if (retval != NULL)
{
sw_zval_ptr_dtor(&retval);
}
sw_zval_ptr_dtor(&result);
if (client->connector.error_code > 0)
{
retval = NULL;
//close
sw_zend_call_method_with_0_params(&zobject, swoole_mysql_class_entry_ptr, NULL, "close", &retval);
if (retval)
{
sw_zval_ptr_dtor(&retval);
}
}
}
static int swoole_mysql_onWrite(swReactor *reactor, swEvent *event)
{
if (event->socket->active)
{
return swReactor_onWrite(SwooleG.main_reactor, event);
}
socklen_t len = sizeof(SwooleG.error);
if (getsockopt(event->fd, SOL_SOCKET, SO_ERROR, &SwooleG.error, &len) < 0)
{
swWarn("getsockopt(%d) failed. Error: %s[%d]", event->fd, strerror(errno), errno);
return SW_ERR;
}
mysql_client *client = event->socket->object;
//success
if (SwooleG.error == 0)
{
//listen read event
SwooleG.main_reactor->set(SwooleG.main_reactor, event->fd, PHP_SWOOLE_FD_MYSQL | SW_EVENT_READ);
//connected
event->socket->active = 1;
client->handshake = SW_MYSQL_HANDSHAKE_WAIT_REQUEST;
}
else
{
client->connector.error_code = SwooleG.error;
client->connector.error_msg = strerror(SwooleG.error);
client->connector.error_length = strlen(client->connector.error_msg);
swoole_mysql_onConnect(client TSRMLS_CC);
}
return SW_OK;
}
static int swoole_mysql_onHandShake(mysql_client *client TSRMLS_DC)
{
swString *buffer = client->buffer;
swClient *cli = client->cli;
mysql_connector *connector = &client->connector;
int n = cli->recv(cli, buffer->str + buffer->length, buffer->size - buffer->length, 0);
if (n < 0)
{
switch (swConnection_error(errno))
{
case SW_ERROR:
swSysError("Read from socket[%d] failed.", cli->socket->fd);
return SW_ERR;
case SW_CLOSE:
goto system_call_error;
case SW_WAIT:
return SW_OK;
default:
return SW_ERR;
}
}
else if (n == 0)
{
errno = ECONNRESET;
goto system_call_error;
}
buffer->length += n;
int ret = 0;
_again:
swTraceLog(SW_TRACE_MYSQL_CLIENT, "handshake on %d", client->handshake);
if (client->switch_check)
{
// after handshake we need check if server request us to switch auth type first
goto _check_switch;
}
switch(client->handshake)
{
case SW_MYSQL_HANDSHAKE_WAIT_REQUEST:
{
client->switch_check = 1;
ret = mysql_handshake(connector, buffer->str, buffer->length);
if (ret < 0)
{
goto _error;
}
else if (ret > 0)
{
_send:
if (cli->send(cli, connector->buf, connector->packet_length + 4, 0) < 0)
{
system_call_error: connector->error_code = errno;
connector->error_msg = strerror(errno);
connector->error_length = strlen(connector->error_msg);
swoole_mysql_onConnect(client TSRMLS_CC);
return SW_OK;
}
else
{
// clear for the new package
swString_clear(buffer);
// mysql_handshake will return the next state flag
client->handshake = ret;
}
}
break;
}
case SW_MYSQL_HANDSHAKE_WAIT_SWITCH:
{
_check_switch:
client->switch_check = 0;
int next_state;
// handle auth switch request
switch (next_state = mysql_auth_switch(connector, buffer->str, buffer->length))
{
case SW_AGAIN:
return SW_OK;
case SW_ERR:
// not the switch package, go to the next
goto _again;
default:
ret = next_state;
goto _send;
}
break;
}
case SW_MYSQL_HANDSHAKE_WAIT_SIGNATURE:
{
switch (mysql_parse_auth_signature(buffer, connector))
{
case SW_MYSQL_AUTH_SIGNATURE_SUCCESS:
{
client->handshake = SW_MYSQL_HANDSHAKE_WAIT_RESULT;
break;
}
case SW_MYSQL_AUTH_SIGNATURE_FULL_AUTH_REQUIRED:
{
// send response and wait RSA public key
ret = SW_MYSQL_HANDSHAKE_WAIT_RSA; // handshake = ret
goto _send;
}
default:
{
goto _error;
}
}
// may be more packages
if (buffer->offset < buffer->length)
{
goto _again;
}
else
{
swString_clear(buffer);
}
break;
}
case SW_MYSQL_HANDSHAKE_WAIT_RSA:
{
// encode by RSA
#ifdef SW_MYSQL_RSA_SUPPORT
switch (mysql_parse_rsa(connector, SWSTRING_CURRENT_VL(buffer)))
{
case SW_AGAIN:
return SW_OK;
case SW_OK:
ret = SW_MYSQL_HANDSHAKE_WAIT_RESULT; // handshake = ret
goto _send;
default:
goto _error;
}
#else
connector->error_code = -1;
connector->error_msg = "MySQL8 RSA-Auth need enable OpenSSL!";
connector->error_length = strlen(connector->error_msg);
swoole_mysql_onConnect(client TSRMLS_CC);
return SW_OK;
#endif
break;
}
default:
{
ret = mysql_get_result(connector, SWSTRING_CURRENT_VL(buffer));
if (ret < 0)
{
_error:
swoole_mysql_onConnect(client TSRMLS_CC);
}
else if (ret > 0)
{
swString_clear(buffer);
client->handshake = SW_MYSQL_HANDSHAKE_COMPLETED;
swoole_mysql_onConnect(client TSRMLS_CC);
}
// else recv again
}
}
return SW_OK;
}
static int swoole_mysql_onRead(swReactor *reactor, swEvent *event)
{
mysql_client *client = event->socket->object;
if (client->handshake != SW_MYSQL_HANDSHAKE_COMPLETED)
{
return swoole_mysql_onHandShake(client TSRMLS_CC);
}
int sock = event->fd;
int ret;
zval *zobject = client->object;
swString *buffer = client->buffer;
zval **args[2];
zval *callback = NULL;
zval *retval = NULL;
zval *result = NULL;
while(1)
{
ret = recv(sock, buffer->str + buffer->length, buffer->size - buffer->length, 0);
if (ret < 0)
{
if (errno == EINTR)
{
continue;
}
else
{
switch (swConnection_error(errno))
{
case SW_ERROR:
swSysError("Read from socket[%d] failed.", event->fd);
return SW_ERR;
case SW_CLOSE:
goto close_fd;
case SW_WAIT:
goto parse_response;
default:
return SW_ERR;
}
}
}
else if (ret == 0)
{
close_fd:
if (client->state == SW_MYSQL_STATE_READ_END)
{
goto parse_response;
}
sw_zend_call_method_with_0_params(&zobject, swoole_mysql_class_entry_ptr, NULL, "close", &retval);
if (retval)
{
sw_zval_ptr_dtor(&retval);
}
return SW_OK;
}
else
{
buffer->length += ret;
//recv again
if (buffer->length == buffer->size)
{
if (swString_extend(buffer, buffer->size * 2) < 0)
{
swoole_php_fatal_error(E_ERROR, "malloc failed.");
reactor->del(SwooleG.main_reactor, event->fd);
}
continue;
}
parse_response:
if (mysql_response(client) < 0)
{
return SW_OK;
}
zend_update_property_long(swoole_mysql_class_entry_ptr, zobject, ZEND_STRL("affected_rows"), client->response.affected_rows TSRMLS_CC);
zend_update_property_long(swoole_mysql_class_entry_ptr, zobject, ZEND_STRL("insert_id"), client->response.insert_id TSRMLS_CC);
client->state = SW_MYSQL_STATE_QUERY;
args[0] = &zobject;
//OK
if (client->response.response_type == 0)
{
SW_ALLOC_INIT_ZVAL(result);
ZVAL_BOOL(result, 1);
}
//ERROR
else if (client->response.response_type == 255)
{
SW_ALLOC_INIT_ZVAL(result);
ZVAL_BOOL(result, 0);
zend_update_property_stringl(swoole_mysql_class_entry_ptr, zobject, ZEND_STRL("error"), client->response.server_msg, client->response.l_server_msg TSRMLS_CC);
zend_update_property_long(swoole_mysql_class_entry_ptr, zobject, ZEND_STRL("errno"), client->response.error_code TSRMLS_CC);
}
//ResultSet
else
{
result = client->response.result_array;
}
args[1] = &result;
callback = client->callback;
if (sw_call_user_function_ex(EG(function_table), NULL, callback, &retval, 2, args, 0, NULL TSRMLS_CC) != SUCCESS)
{
swoole_php_fatal_error(E_WARNING, "swoole_async_mysql callback[2] handler error.");
reactor->del(SwooleG.main_reactor, event->fd);
}
if (EG(exception))
{
zend_exception_error(EG(exception), E_ERROR TSRMLS_CC);
}
/* free memory */
if (retval)
{
sw_zval_ptr_dtor(&retval);
}
if (result)
{
sw_zval_free(result);
}
//free callback object
sw_zval_free(callback);
swConnection *_socket = swReactor_get(SwooleG.main_reactor, event->fd);
if (_socket->object)
{
//clear buffer
swString_clear(client->buffer);
bzero(&client->response, sizeof(client->response));
}
return SW_OK;
}
}
return SW_OK;
}
#ifdef SW_USE_MYSQLND
static PHP_METHOD(swoole_mysql, escape)
{
swString str;
bzero(&str, sizeof(str));
long flags;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", &str.str, &str.length, &flags) == FAILURE)
{
return;
}
if (str.length <= 0)
{
swoole_php_fatal_error(E_WARNING, "String is empty.");
RETURN_FALSE;
}
mysql_client *client = swoole_get_object(getThis());
if (!client)
{
swoole_php_fatal_error(E_WARNING, "object is not instanceof swoole_mysql.");
RETURN_FALSE;
}
if (!client->cli)
{
swoole_php_fatal_error(E_WARNING, "mysql connection#%d is closed.", client->fd);
RETURN_FALSE;
}
char *newstr = safe_emalloc(2, str.length + 1, 1);
if (newstr == NULL)
{
swoole_php_fatal_error(E_ERROR, "emalloc(%ld) failed.", str.length + 1);
RETURN_FALSE;
}
const MYSQLND_CHARSET* cset = mysqlnd_find_charset_nr(client->connector.character_set);
if (cset == NULL)
{
swoole_php_fatal_error(E_ERROR, "unknown mysql charset[%d].", client->connector.character_set);
RETURN_FALSE;
}
int newstr_len = mysqlnd_cset_escape_slashes(cset, newstr, str.str, str.length TSRMLS_CC);
if (newstr_len < 0)
{
swoole_php_fatal_error(E_ERROR, "mysqlnd_cset_escape_slashes() failed.");
RETURN_FALSE;
}
SW_RETURN_STRINGL(newstr, newstr_len, 0);
}
#endif
C
1
https://gitee.com/github-22386239213213/swoole.git
git@gitee.com:github-22386239213213/swoole.git
github-22386239213213
swoole
swoole-src
master

搜索帮助