Пример #1
0
        public static int SizeForType(MYSQL_FIELD *field)
        {
            switch (field->type)
            {
            case MYSQL_TYPE_NULL:
                return(0);

            case MYSQL_TYPE_TINY:
                return(1);

            case MYSQL_TYPE_YEAR:
            case MYSQL_TYPE_SHORT:
                return(2);

            case MYSQL_TYPE_INT24:
            case MYSQL_TYPE_LONG:
            case MYSQL_TYPE_FLOAT:
                return(4);

            case MYSQL_TYPE_DOUBLE:
            case MYSQL_TYPE_LONGLONG:
            case MYSQL_TYPE_BIT:
                return(8);

            case MYSQL_TYPE_TIMESTAMP:
            case MYSQL_TYPE_DATE:
            case MYSQL_TYPE_TIME:
            case MYSQL_TYPE_DATETIME:
                return(sizeof(MYSQL_TIME));

            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:
                return((int)(field->max_length).Value + 1);

            case MYSQL_TYPE_DECIMAL:
            case MYSQL_TYPE_NEWDECIMAL:
                return(64);

            case MYSQL_TYPE_GEOMETRY:
            /*
             * Following types are not sent over the wire:
             * MYSQL_TYPE_ENUM:
             * MYSQL_TYPE_SET:
             */
            default:
                FEL_LOG_WARN("sql.sql", "SQL::SizeForType(): invalid field type {0}", field->type);
                return(0);
            }
        }
Пример #2
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]);
            }
        }
Пример #3
0
        bool _Query(string sql, ref IntPtr pResult, ref MYSQL_FIELD *pFields, ref long pRowCount, ref int pFieldCount)
        {
            if (_mysql == IntPtr.Zero)
            {
                return(false);
            }

            {
                var _s = GetMSTime();

                if (mysql_query(_mysql, sql) != 0)
                {
                    var lErrno = mysql_errno(_mysql);
                    FEL_LOG_INFO("sql.sql", "SQL: {0}", sql);
                    FEL_LOG_ERROR("sql.sql", "[{0}] {1}", lErrno, mysql_error(_mysql));

                    if (_HandleMySqlErrno(lErrno))                                                     // If it returns true, an error was handled successfully (i.e. reconnection)
                    {
                        return(_Query(sql, ref pResult, ref pFields, ref pRowCount, ref pFieldCount)); // We try again
                    }
                    return(false);
                }
                else
                {
                    FEL_LOG_DEBUG("sql.sql", "[{0} ms] SQL: {1}", GetMSTimeDiff(_s, GetMSTime()), sql);
                }

                pResult     = mysql_store_result(_mysql);
                pRowCount   = mysql_affected_rows(_mysql);
                pFieldCount = mysql_field_count(_mysql);
            }

            if ((IntPtr)pResult == IntPtr.Zero)
            {
                return(false);
            }

            if (pRowCount == 0)
            {
                mysql_free_result(pResult);
                return(false);
            }

            pFields = mysql_fetch_fields(pResult);

            return(true);
        }
Пример #4
0
        public QueryResult?Query(string sql)
        {
            if (string.IsNullOrEmpty(sql))
            {
                return(null);
            }

            IntPtr       result     = default;
            MYSQL_FIELD *fields     = default;
            long         rowCount   = 0;
            int          fieldCount = 0;

            if (!_Query(sql, ref result, ref fields, ref rowCount, ref fieldCount))
            {
                return(null);
            }

            return(new QueryResult(result, fields, rowCount, fieldCount));
        }
Пример #5
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);
Пример #6
0
 public static void InitializeDatabaseFieldMetadata(QueryResultFieldMetadata *meta, MYSQL_FIELD *field, int fieldIndex)
 {
     meta->TableName  = (IntPtr)field->org_table;
     meta->TableAlias = (IntPtr)field->table;
     meta->Name       = (IntPtr)field->org_name;
     meta->Alias      = (IntPtr)field->name;
     meta->FieldType  = field->type;
     meta->Index      = fieldIndex;
     meta->Type       = MySqlTypeToFieldType(field->type);
 }