Ejemplo n.º 1
0
        public QueryResult(IntPtr result, MYSQL_FIELD *fields, long rowCount, int fieldCount)
        {
            _result     = result;
            _fields     = fields;
            _rowCount   = rowCount;
            _fieldCount = fieldCount;

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

            for (int i = 0; i < _fieldCount; i++)
            {
                InitializeDatabaseFieldMetadata(&_fieldMetadata[i], &_fields[i], i);
                _currentRow[i].SetMetadata(&_fieldMetadata[i]);
            }
        }
Ejemplo n.º 2
0
        void CleanUp()
        {
            if (_currentRow != default)
            {
                Marshal.FreeHGlobal((IntPtr)_currentRow);
                _currentRow = default;
            }

            if (_fieldMetadata != default)
            {
                Marshal.FreeHGlobal((IntPtr)_fieldMetadata);
                _fieldMetadata = default;
            }

            if (_result != default)
            {
                mysql_free_result(_result);
                _result = default;
            }
        }
Ejemplo 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);