Exemplo n.º 1
0
        bool _Query(PreparedStatementBase stmt, ref MySqlPreparedStatement?mysqlStmt, ref IntPtr pResult, ref long pRowCount, ref int pFieldCount)
        {
            if (_mysql == default)
            {
                return(false);
            }

            var index = stmt.Index;

            var mStmt = GetPreparedStatement(index);

            Assert(mStmt != null);            // Can only be null if preparation failed, server side error or bad query

            mStmt !.BindParameters(stmt);
            mysqlStmt = mStmt;

            var         msql_STMT = mStmt.STMT;
            MYSQL_BIND *msql_BIND = mStmt.Bind;

            var _s = GetMSTime();

            if (mysql_stmt_bind_param(msql_STMT, msql_BIND))
            {
                var lErrno = mysql_errno(_mysql);
                FEL_LOG_ERROR("sql.sql", "SQL(p): {0}\n [ERROR]: [{1}] {2}", mStmt.GetQueryString(), lErrno, mysql_stmt_error(msql_STMT));

                if (_HandleMySqlErrno(lErrno))                                                        // If it returns true, an error was handled successfully (i.e. reconnection)
                {
                    return(_Query(stmt, ref mysqlStmt, ref pResult, ref pRowCount, ref pFieldCount)); // Try again
                }
                mStmt.ClearParameters();
                return(false);
            }

            if (mysql_stmt_execute(msql_STMT) != 0)
            {
                var lErrno = mysql_errno(_mysql);
                FEL_LOG_ERROR("sql.sql", "SQL(p): {0}\n [ERROR]: [{1}] {2}", mStmt.GetQueryString(), lErrno, mysql_stmt_error(msql_STMT));

                if (_HandleMySqlErrno(lErrno))                                                        // If it returns true, an error was handled successfully (i.e. reconnection)
                {
                    return(_Query(stmt, ref mysqlStmt, ref pResult, ref pRowCount, ref pFieldCount)); // Try again
                }
                mStmt.ClearParameters();
                return(false);
            }

            FEL_LOG_DEBUG("sql.sql", "[{0} ms] SQL(p): {1}", GetMSTimeDiff(_s, GetMSTime()), mStmt.GetQueryString());

            mStmt.ClearParameters();

            pResult     = mysql_stmt_result_metadata(msql_STMT);
            pRowCount   = mysql_stmt_num_rows(msql_STMT);
            pFieldCount = mysql_stmt_field_count(msql_STMT);

            return(true);
        }
Exemplo n.º 2
0
        public MySqlPreparedStatement(IntPtr stmt, string queryString)
        {
            _Mstmt       = stmt;
            _queryString = queryString;
            _paramCount  = mysql_stmt_param_count(stmt);
            _paramsSet   = new bool[_paramCount];

            _bind = (MYSQL_BIND *)Marshal.AllocHGlobal(sizeof(MYSQL_BIND) * _paramCount);
            new Span <byte>(_bind, sizeof(MYSQL_BIND) * _paramCount).Fill(0);

            // "If set to true, causes mysql_stmt_store_result() to update the metadata MYSQL_FIELD->max_length value."
            mysql_stmt_attr_set(stmt, STMT_ATTR_UPDATE_MAX_LENGTH, true);
        }
Exemplo n.º 3
0
        public const int UNSIGNED_FLAG = 32;    /**< Field is unsigned */

        public PreparedQueryResult(IntPtr stmt, IntPtr result, long rowCount, int fieldCount)
        {
            _stmt           = stmt;
            _rowCount       = rowCount;
            _fieldCount     = fieldCount;
            _metadataResult = result;

            if (_metadataResult == default)
            {
                return;
            }

            if (DatabaseLoader.IsMySQL8)
            {
                if (((MYSQL_STMT *)_stmt)->bind_result_done != 0)
                {
                    Marshal.FreeHGlobal((IntPtr)((MYSQL_STMT *)_stmt)->bind->length);
                    Marshal.FreeHGlobal((IntPtr)((MYSQL_STMT *)_stmt)->bind->is_null);
                }
            }
            else
            {
                if (((MYSQL_STMT_OLD *)_stmt)->bind_result_done != 0)
                {
                    Marshal.FreeHGlobal((IntPtr)((MYSQL_STMT_OLD *)_stmt)->bind->length);
                    Marshal.FreeHGlobal((IntPtr)((MYSQL_STMT_OLD *)_stmt)->bind->is_null);
                }
            }

            _rBind = (MYSQL_BIND *)Marshal.AllocHGlobal(sizeof(MYSQL_BIND) * _fieldCount);

            //- for future readers wondering where the f**k this is freed - mysql_stmt_bind_result moves pointers to these
            // from m_rBind to m_stmt->bind and it is later freed by the `if (m_stmt->bind_result_done)` block just above here
            // MYSQL_STMT lifetime is equal to connection lifetime
            var isNullBuffer = (bool *)Marshal.AllocHGlobal(sizeof(bool) * _fieldCount);
            var lengthBuffer = (CULong *)Marshal.AllocHGlobal(sizeof(CULong) * _fieldCount);

            new Span <byte>(_rBind, sizeof(MYSQL_BIND) * _fieldCount).Fill(0);
            new Span <byte>(isNullBuffer, sizeof(bool) * _fieldCount).Fill(0);
            new Span <byte>(lengthBuffer, sizeof(CULong) * _fieldCount).Fill(0);

            //- This is where we store the (entire) resultset
            if (mysql_stmt_store_result(_stmt) != 0)
            {
                FEL_LOG_WARN("sql.sql", "{0}:mysql_stmt_store_result, cannot bind result from MySQL server. Error: {1}", "PreparedQueryResult()", mysql_stmt_error(_stmt));
                Marshal.FreeHGlobal((IntPtr)_rBind);
                Marshal.FreeHGlobal((IntPtr)isNullBuffer);
                Marshal.FreeHGlobal((IntPtr)lengthBuffer);
                _rBind = default;
                return;
            }

            _rowCount = mysql_stmt_num_rows(_stmt);

            //- This is where we prepare the buffer based on metadata
            MYSQL_FIELD *field = mysql_fetch_fields(_metadataResult);

            _fieldMetadata = (QueryResultFieldMetadata *)Marshal.AllocHGlobal(sizeof(QueryResultFieldMetadata) * _fieldCount);

            int rowSize = 0;

            for (int i = 0; i < _fieldCount; ++i)
            {
                int size = SizeForType(&field[i]);
                rowSize += size;

                InitializeDatabaseFieldMetadata(&_fieldMetadata[i], &field[i], i);

                _rBind[i].buffer_type   = field[i].type;
                _rBind[i].buffer_length = new CULong((uint)size);
                _rBind[i].length        = &lengthBuffer[i];
                _rBind[i].is_null       = &isNullBuffer[i];
                _rBind[i].error         = default;
                _rBind[i].is_unsigned   = (field[i].flags & UNSIGNED_FLAG) != 0;
            }

            var dataBuffer = (byte *)Marshal.AllocHGlobal(rowSize * (int)_rowCount);

            for (int i = 0, offset = 0; i < _fieldCount; ++i)
            {
                _rBind[i].buffer = dataBuffer + offset;
                offset          += (int)_rBind[i].buffer_length.Value;
            }

            //- This is where we bind the bind the buffer to the statement
            if (mysql_stmt_bind_result(_stmt, _rBind))
            {
                FEL_LOG_WARN("sql.sql", "{0}:mysql_stmt_bind_result, cannot bind result from MySQL server. Error: {1}", "PreparedQueryResult()", mysql_stmt_error(_stmt));
                mysql_stmt_free_result(_stmt);
                CleanUp();
                Marshal.FreeHGlobal((IntPtr)isNullBuffer);
                Marshal.FreeHGlobal((IntPtr)lengthBuffer);
                return;
            }

            _rows = (Field *)Marshal.AllocHGlobal(sizeof(Field) * _fieldCount * (int)_rowCount);

            while (_NextRow())
            {
                for (int fIndex = 0; fIndex < _fieldCount; ++fIndex)
                {
                    _rows[(int)_rowPosition * _fieldCount + fIndex].SetMetadata(&_fieldMetadata[fIndex]);

                    var buffer_length  = (int)_rBind[fIndex].buffer_length.Value;
                    var fetched_length = (int)(*_rBind[fIndex].length).Value;
                    if (!*_rBind[fIndex].is_null)
                    {
                        void *buffer = DatabaseLoader.IsMySQL8 ? ((MYSQL_STMT *)_stmt)->bind[fIndex].buffer : ((MYSQL_STMT_OLD *)_stmt)->bind[fIndex].buffer;
                        switch (_rBind[fIndex].buffer_type)
                        {
                        case MYSQL_TYPE_TINY_BLOB:
                        case MYSQL_TYPE_MEDIUM_BLOB:
                        case MYSQL_TYPE_LONG_BLOB:
                        case MYSQL_TYPE_BLOB:
                        case MYSQL_TYPE_STRING:
                        case MYSQL_TYPE_VAR_STRING:
                            // warning - the string will not be null-terminated if there is no space for it in the buffer
                            // when mysql_stmt_fetch returned MYSQL_DATA_TRUNCATED
                            // we cannot blindly null-terminate the data either as it may be retrieved as binary blob and not specifically a string
                            // in this case using Field::GetCString will result in garbage
                            if (fetched_length < buffer_length)
                            {
                                *((byte *)buffer + fetched_length) = 0;
                            }
                            break;

                        default:
                            break;
                        }

                        _rows[(int)_rowPosition * _fieldCount + fIndex].SetByteValue((byte *)buffer, fetched_length);

                        // move buffer pointer to next part
                        if (DatabaseLoader.IsMySQL8)
                        {
                            ((MYSQL_STMT *)_stmt)->bind[fIndex].buffer = (byte *)buffer + rowSize;
                        }
                        else
                        {
                            ((MYSQL_STMT_OLD *)_stmt)->bind[fIndex].buffer = (byte *)buffer + rowSize;
                        }
                    }
                    else
                    {
                        _rows[(int)_rowPosition * _fieldCount + fIndex].SetByteValue(default, (int)(*_rBind[fIndex].length).Value);