void MADB_CleanBulkOperData(MADB_Stmt *Stmt, unsigned int ParamOffset) { if (MADB_DOING_BULK_OPER(Stmt)) { MADB_DescRecord *CRec; void *DataPtr= NULL; MYSQL_BIND *MaBind= NULL; int i; for (i= ParamOffset; i < MADB_STMT_PARAM_COUNT(Stmt); ++i) { if (CRec= MADB_DescGetInternalRecord(Stmt->Apd, i, MADB_DESC_READ)) { MaBind= &Stmt->params[i - ParamOffset]; DataPtr= GetBindOffset(Stmt->Apd, CRec, CRec->DataPtr, 0, CRec->OctetLength); if (MaBind->buffer != DataPtr) { switch (CRec->ConciseType) { case DATETIME_TYPES: if (CanUseStructArrForDatetime(Stmt) == FALSE) { MADB_FREE(MaBind->buffer); break; } /* Otherwise falling through and do the same as for others */ case SQL_C_WCHAR: case SQL_C_NUMERIC: { unsigned int i; for (i= 0; i < Stmt->Bulk.ArraySize; ++i) { MADB_FREE(((char**)MaBind->buffer)[i]); } } /* falling through */ default: MADB_FREE(MaBind->buffer); } } MADB_FREE(MaBind->length); MADB_FREE(MaBind->u.indicator); } } Stmt->Bulk.ArraySize= 0; Stmt->Bulk.HasRowsToSkip= 0; } }
/* {{{ MADB_DescSetIrdMetadata */ my_bool MADB_DescSetIrdMetadata(MADB_Stmt *Stmt, MYSQL_FIELD *Fields, unsigned int NumFields) { SQLUINTEGER i; for (i=0; i < NumFields; i++) { if (MADB_SetIrdRecord(Stmt, MADB_DescGetInternalRecord(Stmt->Ird, i, MADB_DESC_WRITE), &Fields[i])) { return 1; } } return 0; }
SQLRETURN MADB_DescGetRec(MADB_Desc *Desc, SQLSMALLINT RecNumber, SQLCHAR *Name, SQLSMALLINT BufferLength, SQLSMALLINT *StringLengthPtr, SQLSMALLINT *TypePtr, SQLSMALLINT *SubTypePtr, SQLLEN *LengthPtr, SQLSMALLINT *PrecisionPtr, SQLSMALLINT *ScalePtr, SQLSMALLINT *NullablePtr, BOOL isWChar) { MADB_DescRecord *Record; SQLLEN Length; MADB_CLEAR_ERROR(&Desc->Error); if (!(Record= MADB_DescGetInternalRecord(Desc, RecNumber, MADB_DESC_READ))) { MADB_SetError(&Desc->Error, MADB_ERR_07009, NULL, 0); return Desc->Error.ReturnValue; } /* SQL_DESC_NAME */ Length= MADB_SetString(isWChar ? CP_UTF8 : 0, Name, BufferLength, Record->BaseColumnName, SQL_NTS, &Desc->Error); if (StringLengthPtr) *StringLengthPtr= (SQLSMALLINT)Length; Record->Unnamed= SQL_NAMED; /* SQL_DESC_TYPE */ *(SQLSMALLINT *)TypePtr= (SQLSMALLINT)Record->Type; /* SQL_DESC_DATETIME_INTERVAL_CODE */ *(SQLSMALLINT *)SubTypePtr= Record->DateTimeIntervalCode; /* SQL_DESC_OCTET_LENGTH */ *(SQLLEN *)LengthPtr= (SQLLEN)Record->OctetLength; /* SQL_DESC_PRECISION */ *(SQLSMALLINT *)PrecisionPtr= (SQLSMALLINT)Record->Precision; /* SQL_DESC_SCALE */ *(SQLSMALLINT *)ScalePtr= (SQLSMALLINT)Record->Scale; /* SQL_DESC_NULLABLE */ *(SQLSMALLINT *)NullablePtr= (SQLSMALLINT)Record->Nullable; return SQL_SUCCESS; }
MYSQL_RES *MADB_GetDefaultColumnValues(MADB_Stmt *Stmt, MYSQL_FIELD *fields) { MADB_DynString DynStr; unsigned int i; MYSQL_RES *result= NULL; MADB_InitDynamicString(&DynStr, "SELECT COLUMN_NAME, COLUMN_DEFAULT FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA='", 512, 512); if (MADB_DynstrAppend(&DynStr, fields[0].db) || MADB_DynstrAppend(&DynStr, "' AND TABLE_NAME='") || MADB_DynstrAppend(&DynStr, fields[0].org_table) || MADB_DynstrAppend(&DynStr, "' AND COLUMN_NAME IN (")) goto error; for (i=0; i < mysql_stmt_field_count(Stmt->stmt); i++) { MADB_DescRecord *Rec= MADB_DescGetInternalRecord(Stmt->Ard, i, MADB_DESC_READ); if (!Rec->inUse || MADB_ColumnIgnoredInAllRows(Stmt->Ard, Rec) == TRUE) { continue; } if (MADB_DynstrAppend(&DynStr, i > 0 ? ",'" : "'") || MADB_DynstrAppend(&DynStr, fields[i].org_name) || MADB_DynstrAppend(&DynStr, "'")) { goto error; } } if (MADB_DynstrAppend(&DynStr, ") AND COLUMN_DEFAULT IS NOT NULL")) goto error; LOCK_MARIADB(Stmt->Connection); if (mysql_query(Stmt->Connection->mariadb, DynStr.str)) goto error; result= mysql_store_result(Stmt->Connection->mariadb); error: UNLOCK_MARIADB(Stmt->Connection); MADB_DynstrFree(&DynStr); return result; }
/* {{{ MADB_FixColumnDataTypes */ my_bool MADB_FixColumnDataTypes(MADB_Stmt *Stmt, MADB_ShortTypeInfo *ColTypesArr) { SQLUINTEGER i; MADB_DescRecord *Record= NULL; if (ColTypesArr == NULL) { return 1; } for (i=0; i < Stmt->Ird->Header.Count; ++i) { if (ColTypesArr[i].SqlType != 0) { Record= MADB_DescGetInternalRecord(Stmt->Ird, i, MADB_DESC_READ); if (Record == NULL) { return 1; } Record->ConciseType= ColTypesArr[i].SqlType; Record->Nullable= ColTypesArr[i].Nullable; Record->Unsigned= ColTypesArr[i].Unsigned != 0 ? SQL_TRUE : SQL_FALSE; if (ColTypesArr[i].OctetLength > 0) { Record->OctetLength= ColTypesArr[i].OctetLength; } if (MADB_FixIrdRecord(Stmt, Record)) { return 1; } } } /* If the stmt is re-executed, we should be able to fix columns again */ Stmt->ColsTypeFixArr= ColTypesArr; return 0; }
int MADB_FindNextDaeParam(MADB_Desc *Desc, int InitialParam, SQLSMALLINT RowNumber) { int i; MADB_DescRecord *Record; for (i= InitialParam > -1 ? InitialParam + 1 : 0; i < Desc->Header.Count; i++) { if ((Record= MADB_DescGetInternalRecord(Desc, i, MADB_DESC_READ))) { if (Record->OctetLengthPtr) { /* Stmt->DaeRowNumber is 1 based */ SQLLEN *OctetLength = (SQLLEN *)GetBindOffset(Desc, Record, Record->OctetLengthPtr, RowNumber > 1 ? RowNumber - 1 : 0, sizeof(SQLLEN)); if (PARAM_IS_DAE(OctetLength)) { return i; } } } } return MADB_NOPARAM; }
/* Assuming that bulk insert can't go with DAE(and that unlikely ever changes). And that it has been checked before this call, and we can't have DAE here */ SQLRETURN MADB_ExecuteBulk(MADB_Stmt *Stmt, unsigned int ParamOffset) { unsigned int i, IndIdx= -1; unsigned long Dummy; for (i= ParamOffset; i < ParamOffset + MADB_STMT_PARAM_COUNT(Stmt); ++i) { MADB_DescRecord *CRec, *SqlRec; SQLLEN *IndicatorPtr= NULL; SQLLEN *OctetLengthPtr= NULL; void *DataPtr= NULL; MYSQL_BIND *MaBind= &Stmt->params[i - ParamOffset]; SQLULEN row, Start= Stmt->ArrayOffset; if ((CRec= MADB_DescGetInternalRecord(Stmt->Apd, i, MADB_DESC_READ)) && (SqlRec= MADB_DescGetInternalRecord(Stmt->Ipd, i, MADB_DESC_READ))) { /* check if parameter was bound */ if (!CRec->inUse) { return MADB_SetError(&Stmt->Error, MADB_ERR_07002, NULL, 0); } if (MADB_ConversionSupported(CRec, SqlRec) == FALSE) { return MADB_SetError(&Stmt->Error, MADB_ERR_07006, NULL, 0); } MaBind->length= NULL; IndicatorPtr= (SQLLEN *)GetBindOffset(Stmt->Apd, CRec, CRec->IndicatorPtr, 0, sizeof(SQLLEN)); OctetLengthPtr= (SQLLEN *)GetBindOffset(Stmt->Apd, CRec, CRec->OctetLengthPtr, 0, sizeof(SQLLEN)); DataPtr= GetBindOffset(Stmt->Apd, CRec, CRec->DataPtr, 0, CRec->OctetLength); /* If these are the same pointers, setting indicator to NULL to simplify things a bit */ if (IndicatorPtr == OctetLengthPtr) { IndicatorPtr= NULL; } /* Well, specs kinda say, that both values and lenghts arrays should be set(in instruction to param array operations) But there is no error/sqlstate for the case if any of those pointers is not set. Thus we assume that is possible */ if (DataPtr == NULL) { /* Special case - DataPtr is not set, we treat it as all values are NULL. Setting indicators and moving on next param */ RETURN_ERROR_OR_CONTINUE(MADB_InitIndicatorArray(Stmt, MaBind, MADB_MapIndicatorValue(SQL_NULL_DATA))); continue; } /* Sets Stmt->Bulk.HasRowsToSkip if needed, since it traverses and checks status array anyway */ RETURN_ERROR_OR_CONTINUE(MADB_InitBulkOperBuffers(Stmt, CRec, DataPtr, OctetLengthPtr, IndicatorPtr, SqlRec->ConciseType, MaBind)); if (MaBind->u.indicator != NULL && IndIdx == (unsigned int)-1) { IndIdx= i - ParamOffset; } /* Doing it on last parameter - just to do do this once, and to use already allocated indicator array. Little stupid optimization. But it's actually even a bit simpler this way */ if (i == ParamOffset + MADB_STMT_PARAM_COUNT(Stmt) - 1 && Stmt->Bulk.HasRowsToSkip) { if (IndIdx == (unsigned int)-1) { IndIdx= 0; } for (row= Start; row < Start + Stmt->Apd->Header.ArraySize; ++row) { if (Stmt->Apd->Header.ArrayStatusPtr[row] == SQL_PARAM_IGNORE) { MADB_SetIndicatorValue(Stmt, &Stmt->params[IndIdx], (unsigned int)row, SQL_PARAM_IGNORE); } } } if (MADB_AppBufferCanBeUsed(CRec->ConciseType, SqlRec->ConciseType)) { /* Everything has been done for such column already */ continue; } /* We either have skipped rows or need to convert parameter values/convert array */ for (row= Start; row < Start + Stmt->Apd->Header.ArraySize; ++row, DataPtr= (char*)DataPtr + CRec->OctetLength) { void *Buffer= (char*)MaBind->buffer + row*MaBind->buffer_length; void **BufferPtr= (void**)Buffer; /* For the case when Buffer points to the pointer already */ if (Stmt->Apd->Header.ArrayStatusPtr != NULL && Stmt->Apd->Header.ArrayStatusPtr[row] == SQL_PARAM_IGNORE) { continue; } if (MaBind->u.indicator && MaBind->u.indicator[row] > STMT_INDICATOR_NONE) { continue; } switch (CRec->ConciseType) { case SQL_C_CHAR: if (SqlRec->ConciseType != SQL_BIT) { break; } case DATETIME_TYPES: if (CanUseStructArrForDatetime(Stmt)) { BufferPtr= &Buffer; } } /* Need &Dummy here as a length ptr, since NULL is not good here. It would make MADB_ConvertC2Sql to use MaBind->buffer_length by default */ if (!SQL_SUCCEEDED(MADB_ConvertC2Sql(Stmt, CRec, DataPtr, MaBind->length != NULL ? MaBind->length[row] : 0, SqlRec, MaBind, BufferPtr, MaBind->length != NULL ? MaBind->length + row : &Dummy))) { /* Perhaps it's better to move to Clean function */ CRec->InternalBuffer= NULL; return Stmt->Error.ReturnValue; } CRec->InternalBuffer= NULL; } } } return MADB_DoExecute(Stmt, FALSE); }
/* {{{ MADB_DescSetField */ SQLRETURN MADB_DescSetField(SQLHDESC DescriptorHandle, SQLSMALLINT RecNumber, SQLSMALLINT FieldIdentifier, SQLPOINTER ValuePtr, SQLINTEGER BufferLength, my_bool isWChar) { MADB_Desc *Desc= (MADB_Desc *)DescriptorHandle; MADB_DescRecord *DescRecord= NULL; SQLRETURN ret; SQL_UNNAMED; ret= MADB_DeskCheckFldId(Desc, FieldIdentifier, MADB_DESC_WRITE); /* Application may set IPD's field SQL_DESC_UNNAMED to SQL_UNNAMED only */ if (FieldIdentifier == SQL_DESC_UNNAMED && (SQLSMALLINT)ValuePtr == SQL_NAMED) { MADB_SetError(&Desc->Error, MADB_ERR_HY092, NULL, 0); ret= Desc->Error.ReturnValue; } if (!SQL_SUCCEEDED(ret)) return ret; MADB_CLEAR_ERROR(&Desc->Error); switch (FieldIdentifier) { case SQL_DESC_ARRAY_SIZE: Desc->Header.ArraySize= (SQLUINTEGER)ValuePtr; return SQL_SUCCESS; case SQL_DESC_ARRAY_STATUS_PTR: Desc->Header.ArrayStatusPtr= (SQLUSMALLINT *)ValuePtr; return SQL_SUCCESS; case SQL_DESC_BIND_OFFSET_PTR: Desc->Header.BindOffsetPtr= (SQLUINTEGER *)ValuePtr; return SQL_SUCCESS; case SQL_DESC_BIND_TYPE: Desc->Header.BindType= (SQLUINTEGER)ValuePtr; return SQL_SUCCESS; case SQL_DESC_COUNT: Desc->Header.Count= (SQLINTEGER)ValuePtr; return SQL_SUCCESS; case SQL_DESC_ROWS_PROCESSED_PTR: Desc->Header.RowsProcessedPtr= (SQLULEN *)ValuePtr; return SQL_SUCCESS; } if (RecNumber > 0) { if (!(DescRecord= MADB_DescGetInternalRecord(Desc, RecNumber - 1, MADB_DESC_WRITE))) return SQL_ERROR; switch (FieldIdentifier) { case SQL_DESC_CONCISE_TYPE: DescRecord->ConciseType= (SQLSMALLINT)ValuePtr; DescRecord->Type= MADB_GetTypeFromConciseType(DescRecord->ConciseType); break; case SQL_DESC_DATA_PTR: DescRecord->DataPtr= ValuePtr; break; case SQL_DESC_DATETIME_INTERVAL_CODE: DescRecord->DateTimeIntervalCode= (SQLSMALLINT)ValuePtr; break; case SQL_DESC_DATETIME_INTERVAL_PRECISION: DescRecord->DateTimeIntervalPrecision= (SQLINTEGER)ValuePtr; break; case SQL_DESC_FIXED_PREC_SCALE: DescRecord->FixedPrecScale= (SQLSMALLINT)ValuePtr; break; case SQL_DESC_INDICATOR_PTR: DescRecord->IndicatorPtr= (SQLINTEGER *)ValuePtr; break; case SQL_DESC_LENGTH: DescRecord->DescLength= (SQLINTEGER)ValuePtr; break; case SQL_DESC_NUM_PREC_RADIX: DescRecord->NumPrecRadix= (SQLINTEGER)ValuePtr; break; case SQL_DESC_OCTET_LENGTH: DescRecord->OctetLength= (SQLINTEGER)ValuePtr; break; case SQL_DESC_OCTET_LENGTH_PTR: DescRecord->OctetLengthPtr= (SQLINTEGER *)ValuePtr; break; case SQL_DESC_PARAMETER_TYPE: DescRecord->ParameterType= (SQLSMALLINT)ValuePtr; break; case SQL_DESC_PRECISION: DescRecord->Precision= (SQLSMALLINT)ValuePtr; break; case SQL_DESC_SCALE: DescRecord->Scale= (SQLSMALLINT)ValuePtr; break; case SQL_DESC_TYPE: DescRecord->Type= (SQLSMALLINT)ValuePtr; DescRecord->ConciseType= DescRecord->Type; break; } /* bug41018 (ma_desc.c): We need to unbind in case parameter doesn't set a buffer or header field */ switch (FieldIdentifier) { case SQL_DESC_DATA_PTR: case SQL_DESC_OCTET_LENGTH_PTR: case SQL_DESC_INDICATOR_PTR: break; default: if (Desc->DescType== MADB_DESC_ARD && DescRecord && DescRecord->DataPtr) DescRecord->DataPtr= NULL; break; } if (DescRecord) DescRecord->inUse= 1; } return ret; }
/* {{{ MADB_DescGetField */ SQLRETURN MADB_DescGetField(SQLHDESC DescriptorHandle, SQLSMALLINT RecNumber, SQLSMALLINT FieldIdentifier, SQLPOINTER ValuePtr, SQLINTEGER BufferLength, SQLINTEGER *StringLengthPtr, my_bool isWChar) { MADB_Desc *Desc= (MADB_Desc *)DescriptorHandle; MADB_DescRecord *DescRecord= NULL; SQLRETURN ret; size_t Length; /* Bookmark */ if (RecNumber < 1) { /* todo */ } ret= MADB_DeskCheckFldId(Desc, FieldIdentifier, MADB_DESC_READ); if (!SQL_SUCCEEDED(ret)) return ret; MADB_CLEAR_ERROR(&Desc->Error); if (RecNumber) if (!(DescRecord= MADB_DescGetInternalRecord(Desc, RecNumber - 1, MADB_DESC_READ))) return SQL_ERROR; switch (FieldIdentifier) { case SQL_DESC_ALLOC_TYPE: *((SQLINTEGER *)ValuePtr)= Desc->Header.AllocType; break; case SQL_DESC_ARRAY_SIZE: *((SQLULEN *)ValuePtr)= Desc->Header.ArraySize; break; case SQL_DESC_ARRAY_STATUS_PTR: *((SQLPOINTER *)ValuePtr)= (SQLPOINTER)Desc->Header.ArrayStatusPtr; break; case SQL_DESC_BIND_OFFSET_PTR: *((SQLPOINTER *)ValuePtr)= (SQLPOINTER)Desc->Header.BindOffsetPtr; break; case SQL_DESC_BIND_TYPE: *((SQLINTEGER *)ValuePtr)= Desc->Header.BindType; break; case SQL_DESC_COUNT: *(SQLINTEGER *)ValuePtr= Desc->Header.Count; break; case SQL_DESC_ROWS_PROCESSED_PTR: *((SQLPOINTER *)ValuePtr)= (SQLPOINTER)Desc->Header.RowsProcessedPtr; break; case SQL_DESC_AUTO_UNIQUE_VALUE: *((SQLINTEGER *)ValuePtr)= DescRecord->AutoUniqueValue; break; case SQL_DESC_BASE_COLUMN_NAME: Length= MADB_SetString(isWChar ? CP_UTF8 : 0, ValuePtr, BufferLength, DescRecord->BaseColumnName, SQL_NTS, &Desc->Error); if (StringLengthPtr) *StringLengthPtr= Length; break; case SQL_DESC_BASE_TABLE_NAME: Length= MADB_SetString(isWChar ? CP_UTF8 : 0, ValuePtr, BufferLength, DescRecord->BaseTableName, SQL_NTS, &Desc->Error); if (StringLengthPtr) *StringLengthPtr= Length; break; case SQL_DESC_CASE_SENSITIVE: *((SQLINTEGER *)ValuePtr)= DescRecord->CaseSensitive; break; case SQL_DESC_CATALOG_NAME: Length= MADB_SetString(isWChar ? CP_UTF8 : 0, ValuePtr, BufferLength, DescRecord->BaseCatalogName, SQL_NTS, &Desc->Error); if (StringLengthPtr) *StringLengthPtr= Length; break; case SQL_DESC_CONCISE_TYPE: *((SQLSMALLINT *)ValuePtr)= DescRecord->ConciseType; break; case SQL_DESC_DATA_PTR: *((SQLPOINTER *)ValuePtr)= (SQLPOINTER)DescRecord->DataPtr; break; case SQL_DESC_DATETIME_INTERVAL_CODE: *((SQLSMALLINT *)ValuePtr)= DescRecord->DateTimeIntervalCode; break; case SQL_DESC_DATETIME_INTERVAL_PRECISION: *((SQLINTEGER *)ValuePtr)= DescRecord->DateTimeIntervalPrecision; break; case SQL_DESC_FIXED_PREC_SCALE: *((SQLINTEGER *)ValuePtr)= DescRecord->FixedPrecScale; break; case SQL_DESC_INDICATOR_PTR: *((SQLPOINTER *)ValuePtr)= (SQLPOINTER)DescRecord->IndicatorPtr; break; case SQL_DESC_LENGTH: *((SQLINTEGER *)ValuePtr)= DescRecord->DescLength; break; case SQL_DESC_LITERAL_PREFIX: *((SQLPOINTER *)ValuePtr)= (SQLPOINTER)DescRecord->LiteralPrefix; break; case SQL_DESC_LITERAL_SUFFIX: *((SQLPOINTER *)ValuePtr)= (SQLPOINTER)DescRecord->LiteralSuffix; break; case SQL_DESC_LOCAL_TYPE_NAME: Length= MADB_SetString(isWChar ? CP_UTF8 : 0, ValuePtr, BufferLength, DescRecord->LocalTypeName, SQL_NTS, &Desc->Error); if (StringLengthPtr) *StringLengthPtr= Length; break; case SQL_DESC_NAME: Length= MADB_SetString(isWChar ? CP_UTF8 : 0, ValuePtr, BufferLength, DescRecord->BaseColumnName, SQL_NTS, &Desc->Error); if (StringLengthPtr) *StringLengthPtr= Length; DescRecord->Unnamed= SQL_NAMED; break; case SQL_DESC_NULLABLE: *((SQLINTEGER *)ValuePtr)= DescRecord->Nullable; break; case SQL_DESC_NUM_PREC_RADIX: *((SQLINTEGER *)ValuePtr)= DescRecord->NumPrecRadix; break; case SQL_DESC_OCTET_LENGTH: *((SQLINTEGER *)ValuePtr)= DescRecord->OctetLength; break; case SQL_DESC_OCTET_LENGTH_PTR: *((SQLPOINTER *)ValuePtr)= (SQLPOINTER)DescRecord->OctetLengthPtr; break; case SQL_DESC_PARAMETER_TYPE: *((SQLINTEGER *)ValuePtr)= DescRecord->ParameterType; break; case SQL_DESC_PRECISION: *((SQLINTEGER *)ValuePtr)= DescRecord->Precision; break; #if (ODBCVER >= 0x0350) case SQL_DESC_ROWVER: *((SQLPOINTER *)ValuePtr)= (SQLPOINTER)DescRecord->RowVer; break; #endif case SQL_DESC_SCALE: *((SQLINTEGER *)ValuePtr)= DescRecord->Scale; break; case SQL_DESC_SCHEMA_NAME: Length= MADB_SetString(isWChar ? CP_UTF8 : 0, ValuePtr, BufferLength, DescRecord->SchemaName, SQL_NTS, &Desc->Error); if (StringLengthPtr) *StringLengthPtr= Length; break; case SQL_DESC_SEARCHABLE: *((SQLINTEGER *)ValuePtr)= DescRecord->Searchable; break; case SQL_DESC_TABLE_NAME: Length= MADB_SetString(isWChar ? CP_UTF8 : 0, ValuePtr, BufferLength, DescRecord->TableName, SQL_NTS, &Desc->Error); if (StringLengthPtr) *StringLengthPtr= Length; break; case SQL_DESC_TYPE: *((SQLINTEGER *)ValuePtr)= DescRecord->Type; break; case SQL_DESC_TYPE_NAME: *StringLengthPtr= MADB_SetString(isWChar ? CP_UTF8 : 0, ValuePtr, BufferLength, DescRecord->TypeName, SQL_NTS, &Desc->Error); break; case SQL_DESC_UNSIGNED: *((SQLINTEGER *)ValuePtr)= DescRecord->Unsigned; break; case SQL_DESC_UPDATABLE: *((SQLINTEGER *)ValuePtr)= DescRecord->Updateable; break; } return ret; }