Compartir a través de


Función SQLExecDirect

Conformidad
Versión introducida: Cumplimiento de estándares ODBC 1.0: ISO 92

Resumen
SQLExecDirect ejecuta una instrucción preparable, utilizando los valores actuales de las variables de marcador de parámetros si existen parámetros en la instrucción . SQLExecDirect es la forma más rápida de enviar una instrucción SQL para la ejecución única.

Sintaxis

  
SQLRETURN SQLExecDirect(  
     SQLHSTMT     StatementHandle,  
     SQLCHAR *    StatementText,  
     SQLINTEGER   TextLength);  

Argumentos

StatementHandle
[Entrada] Identificador de instrucciones.

StatementText
[Entrada] Instrucción SQL que se va a ejecutar.

TextLength
[Entrada] Longitud de *StatementText en caracteres.

Devoluciones

SQL_SUCCESS, SQL_SUCCESS_WITH_INFO, SQL_NEED_DATA, SQL_STILL_EXECUTING, SQL_ERROR, SQL_NO_DATA, SQL_INVALID_HANDLE o SQL_PARAM_DATA_AVAILABLE.

Diagnóstico

Cuando SQLExecDirect devuelve SQL_ERROR o SQL_SUCCESS_WITH_INFO, se puede obtener un valor SQLSTATE asociado llamando a SQLGetDiagRec con un HandleType de SQL_HANDLE_STMT y un identificador de StatementHandle. En la tabla siguiente se enumeran los valores SQLSTATE devueltos normalmente por SQLExecDirect y se explica cada uno en el contexto de esta función; la notación "(DM)" precede a las descripciones de SQLSTATEs devueltas por el Administrador de controladores. El código de retorno asociado a cada valor SQLSTATE es SQL_ERROR, a menos que se indique lo contrario.

SQLSTATE Error Descripción
01000 Advertencia general Mensaje informativo específico del controlador. (Function devuelve SQL_SUCCESS_WITH_INFO).
01001 Conflicto de la operación del cursor *StatementText contenía una instrucción de actualización o eliminación posicionada, y ninguna fila o más de una fila se actualizaron o eliminaron. (Para obtener más información sobre las actualizaciones de más de una fila, consulte la descripción de la SQL_ATTR_SIMULATE_CURSOR Atributo en SQLSetStmtAttr).

(Function devuelve SQL_SUCCESS_WITH_INFO).
01003 Valor NULL eliminado en la función set El argumento StatementText contenía una función set (como AVG, MAX, MIN, etc.), pero no la función de conjunto COUNT y los valores de argumento NULL se eliminaron antes de aplicar la función. (Function devuelve SQL_SUCCESS_WITH_INFO).
01004 Datos de cadena, truncados a la derecha Los datos binarios o de cadena devueltos para un parámetro de entrada/salida o salida provocaron el truncamiento de datos binarios que no son null o caracteres no NULL. Si era un valor de cadena, se truncaba a la derecha. (Function devuelve SQL_SUCCESS_WITH_INFO).
01006 Privilegio no revocado *StatementText contenía una instrucción REVOKE y el usuario no tenía el privilegio especificado. (Function devuelve SQL_SUCCESS_WITH_INFO).
01007 Privilegio no concedido *StatementText era una instrucción GRANT y el usuario no podía conceder el privilegio especificado.
01S02 Valor de opción cambiado Un atributo de instrucción especificado no era válido debido a las condiciones de trabajo de implementación, por lo que se sustituyó temporalmente un valor similar. (Se puede llamar a SQLGetStmtAttr para determinar cuál es el valor sustituido temporalmente). El valor sustituto es válido para StatementHandle hasta que se cierra el cursor, en cuyo punto el atributo de instrucción vuelve a su valor anterior. Los atributos de instrucción que se pueden cambiar son:

SQL_ ATTR_CONCURRENCY SQL_ ATTR_CURSOR_TYPE SQL_ ATTR_KEYSET_SIZE SQL_ ATTR_MAX_LENGTH SQL_ ATTR_MAX_ROWS SQL_ ATTR_QUERY_TIMEOUT SQL_ ATTR_SIMULATE_CURSOR

(Function devuelve SQL_SUCCESS_WITH_INFO).
01S07 Truncamiento fraccionario Los datos devueltos para un parámetro de entrada/salida o salida se truncaron de modo que la parte fraccionaria de un tipo de datos numérico se truncaba o la parte fraccionaria del componente de tiempo de un tipo de datos time, timestamp o interval se truncaba.

(Function devuelve SQL_SUCCESS_WITH_INFO).
07002 Campo COUNT incorrecto El número de parámetros especificados en SQLBindParameter era menor que el número de parámetros de la instrucción SQL contenida en *StatementText.

Se llamó a SQLBindParameter con ParameterValuePtr establecido en un puntero nulo, StrLen_or_IndPtr no establecido en SQL_NULL_DATA o SQL_DATA_AT_EXEC, y InputOutputType no se estableció en SQL_PARAM_OUTPUT, por lo que el número de parámetros especificados en SQLBindParameter era mayor que el número de parámetros de la instrucción SQL contenida en *StatementText.
07006 Infracción de atributo de tipo de datos restringido No se pudo convertir el valor de datos identificado por el argumento ValueType en SQLBindParameter para el parámetro enlazado al tipo de datos identificado por el argumento ParameterType en SQLBindParameter.

No se pudo convertir el valor de datos devuelto para un parámetro enlazado como SQL_PARAM_INPUT_OUTPUT o SQL_PARAM_OUTPUT al tipo de datos identificado por el argumento ValueType en SQLBindParameter.

(Si los valores de datos de una o varias filas no se pudieron convertir, pero se devolvieron correctamente una o varias filas, esta función devuelve SQL_SUCCESS_WITH_INFO).
07007 Infracción del valor del parámetro restringido El tipo de parámetro SQL_PARAM_INPUT_OUTPUT_STREAM solo se usa para un parámetro que envía y recibe datos en partes. No se permite un búfer enlazado a entrada para este tipo de parámetro.

Este error se producirá cuando se SQL_PARAM_INPUT_OUTPUT el tipo de parámetro y cuando el *StrLen_or_IndPtr especificado en SQLBindParameter no es igual a SQL_NULL_DATA, SQL_DEFAULT_PARAM, SQL_LEN_DATA_AT_EXEC(len) o SQL_DATA_AT_EXEC.
07S01 Uso no válido del parámetro predeterminado Un valor de parámetro, establecido con SQLBindParameter, se SQL_DEFAULT_PARAM y el parámetro correspondiente no tenía un valor predeterminado.
08S01 Error de vínculo de comunicación Se produjo un error en el vínculo de comunicación entre el controlador y el origen de datos al que se conectó el controlador antes de que la función completara el procesamiento.
21S01 La lista de valores de inserción no coincide con la lista de columnas *StatementText contenía una instrucción INSERT y el número de valores que se van a insertar no coincidía con el grado de la tabla derivada.
21S02 El grado de tabla derivada no coincide con la lista de columnas *StatementText contenía una instrucción CREATE VIEW y la lista de columnas no calificadas (el número de columnas especificadas para la vista en los argumentos de identificador de columna de la instrucción SQL) contenía más nombres que el número de columnas de la tabla derivada definida por el argumento de especificación de consulta de la instrucción SQL.
22001 Datos de cadena, truncamiento derecho La asignación de un carácter o valor binario a una columna dio como resultado el truncamiento de datos de caracteres noblank o datos binarios no NULOs.
22002 Variable de indicador necesaria pero no proporcionada Los datos NULL se enlazaban a un parámetro de salida cuyo StrLen_or_IndPtr establecido por SQLBindParameter era un puntero nulo.
22003 Valor numérico fuera del intervalo *StatementText contenía una instrucción SQL que contenía un parámetro numérico enlazado o literal, y el valor provocó que toda la parte (en lugar de fraccionaria) del número se truncase cuando se asignase a la columna de tabla asociada.

Devolver un valor numérico (como numérico o cadena) para uno o varios parámetros de entrada/salida o salida habrían provocado que toda la parte (en lugar de fraccionaria) del número se truncase.
22007 Formato datetime no válido *StatementText contenía una instrucción SQL que contenía una estructura de fecha, hora o marca de tiempo como parámetro enlazado y el parámetro era, respectivamente, una fecha, hora o marca de tiempo no válidas.

Un parámetro de entrada/salida o salida estaba enlazado a una estructura C de fecha, hora o marca de tiempo, y un valor en el parámetro devuelto era, respectivamente, una fecha, hora o marca de tiempo no válidas. (Function devuelve SQL_SUCCESS_WITH_INFO).
22008 Desbordamiento de campo datetime *StatementText contenía una instrucción SQL que contenía una expresión datetime que, cuando se calculaba, daba lugar a una estructura de fecha, hora o marca de tiempo que no era válida.

Una expresión datetime calculada para un parámetro de entrada/salida o salida dio lugar a una estructura C de fecha, hora o marca de tiempo que no era válida.
22012 División por cero *StatementText contenía una instrucción SQL que contenía una expresión aritmética que provocaba la división por cero.

Una expresión aritmética calculada para un parámetro de entrada/salida o salida dio como resultado la división en cero.
22015 Desbordamiento de campo de intervalo *StatementText contenía un parámetro numérico o de intervalo exacto que, cuando se convierte en un tipo de datos SQL de intervalo, provocó una pérdida de dígitos significativos.

*StatementText contenía un parámetro de intervalo con más de un campo que, cuando se convierte en un tipo de datos numérico en una columna, no tenía ninguna representación en el tipo de datos numérico.

*StatementText contenía datos de parámetro que se asignaron a un tipo SQL de intervalo y no había ninguna representación del valor del tipo C en el tipo SQL interval.

La asignación de un parámetro de entrada/salida o salida que era un tipo SQL numérico o de intervalo exacto a un tipo de intervalo C provocó una pérdida de dígitos significativos.

Cuando se asignó un parámetro de entrada/salida o salida a una estructura de intervalo C, no había ninguna representación de los datos en la estructura de datos de intervalo.
22018 Valor de carácter no válido para la especificación de conversión *StatementText contenía un tipo de C que era un numérico exacto o aproximado, un tipo de datos datetime o un tipo de datos interval; el tipo SQL de la columna era un tipo de datos de caracteres; y el valor de la columna no era un literal válido del tipo C enlazado.

Cuando se devolvió un parámetro de entrada/salida o salida, el tipo SQL era un numérico exacto o aproximado, una fecha y hora o un tipo de datos interval; el tipo C se SQL_C_CHAR; y el valor de la columna no era un literal válido del tipo SQL enlazado.
22019 Carácter de escape no válido *StatementText contenía una instrucción SQL que contenía un predicado LIKE con un ESCAPE en la cláusula WHERE y la longitud del carácter de escape que sigue a ESCAPE no era igual a 1.
22025 Secuencia de escape no válida *StatementText contenía una instrucción SQL que contenía "LIKE pattern value ESCAPE escape character" en la cláusula WHERE y el carácter que sigue al carácter de escape en el valor de patrón no era uno de "%" o "_".
23000 Infracción de restricción de integridad *StatementText contenía una instrucción SQL que contenía un parámetro o literal. El valor del parámetro era NULL para una columna definida como NOT NULL en la columna de tabla asociada, se proporcionó un valor duplicado para que una columna restringida contenga solo valores únicos o se infringió alguna otra restricción de integridad.
24000 Estado de cursor no válido Un cursor se colocó en statementHandle por SQLFetch o SQLFetchScroll. El Administrador de controladores devuelve este error si SQLFetch o SQLFetchScroll no ha devuelto SQL_NO_DATA y el controlador devuelve si SQLFetch o SQLFetchScroll ha devuelto SQL_NO_DATA.

Se ha abierto un cursor pero no se ha colocado en StatementHandle.

*StatementText contenía una instrucción de actualización o eliminación posicionada y el cursor se colocaba antes del inicio del conjunto de resultados o después del final del conjunto de resultados.
34 000 Nombre de cursor no válido *StatementText contenía una instrucción update o delete posicionada y el cursor al que hace referencia la instrucción que se ejecuta no estaba abierto.
3D000 Nombre de catálogo no válido El nombre de catálogo especificado en StatementText no era válido.
3F000 Nombre de esquema no válido El nombre de esquema especificado en StatementText no era válido.
40001 Error de serialización La transacción se revierte debido a un interbloqueo de recursos con otra transacción.
40003 Finalización de instrucciones desconocida Error en la conexión asociada durante la ejecución de esta función y no se puede determinar el estado de la transacción.
42000 Error de sintaxis o infracción de acceso *StatementText contenía una instrucción SQL que no era preparable o contenía un error de sintaxis.

El usuario no tenía permiso para ejecutar la instrucción SQL contenida en *StatementText.
42S01 Ya existe una tabla base o vista *StatementText contenía una instrucción CREATE TABLE o CREATE VIEW y el nombre de la tabla o el nombre de vista especificados ya existe.
42S02 Tabla base o vista no encontrada *StatementText contenía una instrucción DROP TABLE o DROP VIEW y el nombre de tabla o vista especificados no existían.

*StatementText contenía una instrucción ALTER TABLE y el nombre de tabla especificado no existía.

*StatementText contenía una instrucción CREATE VIEW y no existía un nombre de tabla o nombre de vista definido por la especificación de consulta.

*StatementText contenía una instrucción CREATE INDEX y el nombre de tabla especificado no existía.

*StatementText contenía una instrucción GRANT o REVOKE y el nombre de la tabla o el nombre de vista especificados no existían.

*StatementText contenía una instrucción SELECT y no existía un nombre de tabla o un nombre de vista especificados.

*StatementText contenía una instrucción DELETE, INSERT o UPDATE y el nombre de tabla especificado no existía.

*StatementText contenía una instrucción CREATE TABLE y una tabla especificada en una restricción (que hace referencia a una tabla distinta de la que se está creando) no existía.

*StatementText contenía una instrucción CREATE SCHEMA y no existía un nombre de tabla o nombre de vista especificado.
42S11 El índice ya existe *StatementText contenía una instrucción CREATE INDEX y el nombre de índice especificado ya existía.

*StatementText contenía una instrucción CREATE SCHEMA y el nombre de índice especificado ya existía.
42S12 No se encontró el índice *StatementText contenía una instrucción DROP INDEX y el nombre de índice especificado no existía.
42S21 La columna ya existe *StatementText contenía una instrucción ALTER TABLE y la columna especificada en la cláusula ADD no es única ni identifica una columna existente en la tabla base.
42S22 No se encontró la columna. *StatementText contenía una instrucción CREATE INDEX y uno o varios de los nombres de columna especificados en la lista de columnas no existían.

*StatementText contenía una instrucción GRANT o REVOKE y no existía un nombre de columna especificado.

*StatementText contenía una instrucción SELECT, DELETE, INSERT o UPDATE y no existía un nombre de columna especificado.

*StatementText contenía una instrucción CREATE TABLE y una columna especificada en una restricción (que hace referencia a una tabla distinta de la que se está creando) no existía.

*StatementText contenía una instrucción CREATE SCHEMA y no existía un nombre de columna especificado.
44 000 Infracción de WITH CHECK OPTION El argumento StatementText contenía una instrucción INSERT realizada en una tabla vista o una tabla derivada de la tabla vista que se creó especificando WITH CHECK OPTION, de modo que una o varias filas afectadas por la instrucción INSERT ya no estarán presentes en la tabla vista.

El argumento StatementText contenía una instrucción UPDATE realizada en una tabla vista o una tabla derivada de la tabla vista que se creó especificando WITH CHECK OPTION, de modo que una o varias filas afectadas por la instrucción UPDATE ya no estarán presentes en la tabla vista.
HY000 Error general Se produjo un error para el que no había ningún SQLSTATE específico y para el que no se definió SQLSTATE específico de la implementación. El mensaje de error devuelto por SQLGetDiagRec en el búfer *MessageText describe el error y su causa.
HY001 Error de asignación de memoria El controlador no pudo asignar memoria necesaria para admitir la ejecución o finalización de la función.
HY008 Operación cancelada El procesamiento asincrónico se ha habilitado para StatementHandle. Se llamó a la función y antes de completar la ejecución, se llamó a SQLCancel o SQLCancelHandle en StatementHandle. A continuación, se llamó a la función de nuevo en StatementHandle.

Se llamó a la función y antes de completar la ejecución, se llamó a SQLCancel o SQLCancelHandle en statementHandle desde un subproceso diferente en una aplicación multiproceso.
HY009 Uso no válido del puntero nulo (DM) *StatementText era un puntero nulo.
HY010 Error de secuencia de funciones (DM) Se llamó a una función de ejecución asincrónica para el identificador de conexión asociado a StatementHandle. Esta función asincrónica todavía se estaba ejecutando cuando se llamó a la función SQLExecDirect .

(DM) SQLExecute, SQLExecDirect o SQLMoreResults se llamó a para statementHandle y devolvió SQL_PARAM_DATA_AVAILABLE. Se llamó a esta función antes de recuperar los datos para todos los parámetros transmitidos.

(DM) Se llamó a una función que ejecuta de forma asincrónica (no esta) para statementHandle y todavía se estaba ejecutando cuando se llamó a esta función.

(DM) SE llamó a SQLExecute, SQLExecDirect, SQLBulkOperations o SQLSetPos para el statementHandle y devolvió SQL_NEED_DATA. Se llamó a esta función antes de enviar datos para todos los parámetros o columnas de datos en ejecución.
HY013 Error de administración de memoria No se pudo procesar la llamada de función porque no se pudo tener acceso a los objetos de memoria subyacentes, posiblemente debido a condiciones de memoria baja.
HY090 Longitud de búfer o cadena no válida (DM) El argumento TextLength era menor o igual que 0, pero no igual a SQL_NTS.

Un valor de parámetro, establecido con SQLBindParameter, era un puntero nulo y el valor de longitud del parámetro no era 0, SQL_NULL_DATA, SQL_DATA_AT_EXEC, SQL_DEFAULT_PARAM o menor o igual que SQL_LEN_DATA_AT_EXEC_OFFSET.

Un valor de parámetro, establecido con SQLBindParameter, no era un puntero nulo; el tipo de datos de C se SQL_C_BINARY o SQL_C_CHAR; y el valor de longitud del parámetro era menor que 0 pero no era SQL_NTS, SQL_NULL_DATA, SQL_DATA_AT_EXEC, SQL_DEFAULT_PARAM, o menor o igual que SQL_LEN_DATA_AT_EXEC_OFFSET.

Un valor de longitud de parámetro enlazado por SQLBindParameter se estableció en SQL_DATA_AT_EXEC; el tipo SQL era SQL_LONGVARCHAR, SQL_LONGVARBINARY o un tipo de datos largo específico del origen de datos; y el tipo de información SQL_NEED_LONG_DATA_LEN en SQLGetInfo era "Y".
HY105 Tipo de parámetro no válido El valor especificado para el argumento InputOutputType en SQLBindParameter se SQL_PARAM_OUTPUT y el parámetro era un parámetro de entrada.
HY109 Posición del cursor no válida *StatementText contenía una instrucción de actualización o eliminación posicionada y el cursor estaba colocado (por SQLSetPos o SQLFetchScroll) en una fila que se había eliminado o no se pudo capturar.
HY117 La conexión se suspende debido al estado de transacción desconocido. Solo se permiten las funciones de desconexión y solo lectura. (DM) Para obtener más información sobre el estado suspendido, vea Función SQLEndTran.
HYC00 Característica opcional no implementada La combinación de la configuración actual de la SQL_ATTR_CONCURRENCY y los atributos de instrucción SQL_ATTR_CURSOR_TYPE no se admiten en el controlador o el origen de datos.

El atributo de instrucción SQL_ATTR_USE_BOOKMARKS se estableció en SQL_UB_VARIABLE y el atributo de instrucción SQL_ATTR_CURSOR_TYPE se estableció en un tipo de cursor para el que el controlador no admite marcadores.
HYT00 Se ha agotado el tiempo de espera El período de tiempo de espera de la consulta expiró antes de que el origen de datos devolva el conjunto de resultados. El período de tiempo de espera se establece mediante SQLSetStmtAttr, SQL_ATTR_QUERY_TIMEOUT.
HYT01 Se ha agotado el tiempo de espera de la conexión. El período de tiempo de espera de conexión expiró antes de que el origen de datos respondiera a la solicitud. El período de tiempo de espera de conexión se establece a través de SQLSetConnectAttr, SQL_ATTR_CONNECTION_TIMEOUT.
IM001 El controlador no admite esta función (DM) El controlador asociado a StatementHandle no admite la función .
IM017 El sondeo está deshabilitado en modo de notificación asincrónica Cada vez que se usa el modelo de notificación, el sondeo está deshabilitado.
IM018 No se ha llamado a SQLCompleteAsync para completar la operación asincrónica anterior en este identificador. Si la llamada de función anterior en el identificador devuelve SQL_STILL_EXECUTING y si el modo de notificación está habilitado, se debe llamar a SQLCompleteAsync en el identificador para realizar el procesamiento posterior y completar la operación.

Comentarios

La aplicación llama a SQLExecDirect para enviar una instrucción SQL al origen de datos. Para obtener más información sobre la ejecución directa, consulte Ejecución directa. El controlador modifica la instrucción para usar el formato de SQL usado por el origen de datos y, a continuación, lo envía al origen de datos. En concreto, el controlador modifica las secuencias de escape usadas para definir determinadas características en SQL. Para obtener la sintaxis de secuencias de escape, vea Secuencias de escape en ODBC.

La aplicación puede incluir uno o varios marcadores de parámetro en la instrucción SQL. Para incluir un marcador de parámetro, la aplicación inserta un signo de interrogación (?) en la instrucción SQL en la posición adecuada. Para obtener información sobre los parámetros, vea Parámetros de instrucción.

Si la instrucción SQL es una instrucción SELECT y la aplicación llamada SQLSetCursorName para asociar un cursor a una instrucción , el controlador usa el cursor especificado. De lo contrario, el controlador genera un nombre de cursor.

Si el origen de datos está en modo de confirmación manual (que requiere inicio explícito de transacciones) y aún no se ha iniciado una transacción, el controlador inicia una transacción antes de enviar la instrucción SQL. Para obtener más información, vea Modo de confirmación manual.

Si una aplicación usa SQLExecDirect para enviar una instrucción COMMIT o ROLLBACK , no será interoperable entre productos DBMS. Para confirmar o revertir una transacción, una aplicación llama a SQLEndTran.

Si SQLExecDirect encuentra un parámetro data-at-execution, devuelve SQL_NEED_DATA. La aplicación envía los datos mediante SQLParamData y SQLPutData. Consulte SQLBindParameter, SQLParamData, SQLPutData y Envío de datos largos.

Si SQLExecDirect ejecuta una instrucción de actualización, inserción o eliminación buscada que no afecta a ninguna fila en el origen de datos, la llamada a SQLExecDirect devuelve SQL_NO_DATA.

Si el valor del atributo de instrucción SQL_ATTR_PARAMSET_SIZE es mayor que 1 y la instrucción SQL contiene al menos un marcador de parámetro, SQLExecDirect ejecutará la instrucción SQL una vez para cada conjunto de valores de parámetro de las matrices a las que apunta el argumento ParameterValuePointer en la llamada a SQLBindParameter. Para obtener más información, vea Matrices de valores de parámetros.

Si los marcadores están activados y se ejecuta una consulta que no admite marcadores, el controlador debe intentar convertir el entorno en uno que admita marcadores cambiando un valor de atributo y devolviendo SQLSTATE 01S02 (valor de opción cambiado). Si el atributo no se puede cambiar, el controlador debe devolver SQLSTATE HY024 (valor de atributo no válido).

Nota:

Al usar la agrupación de conexiones, una aplicación no debe ejecutar instrucciones SQL que cambien la base de datos o el contexto de la base de datos, como la instrucción USE database en SQL Server, que cambia el catálogo usado por un origen de datos.

Ejemplo de código

Vea SQLBindCol, SQLGetData y el programa ODBC de ejemplo.

Para obtener información sobre Vea
Enlazar un búfer a una columna de un conjunto de resultados Función SQLBindCol
Cancelación del procesamiento de instrucciones Función SQLCancel
Ejecución de una operación de confirmación o reversión Función SQLEndTran
Ejecución de una instrucción SQL preparada Función SQLExecute
Captura de varias filas de datos Función SQLFetch
Capturar un bloque de datos o desplazarse por un conjunto de resultados Función SQLFetchScroll
Devolver un nombre de cursor Función SQLGetCursorName
Capturar parte o toda una columna de datos Función SQLGetData
Devolver el siguiente parámetro para enviar datos Función SQLParamData
Preparación de una instrucción para su ejecución Función SQLPrepare
Envío de datos de parámetros en tiempo de ejecución Función SQLPutData
Establecimiento de un nombre de cursor Función SQLSetCursorName
Establecimiento de un atributo de instrucción Función SQLSetStmtAttr

Consulte también

Referencia de API ODBC
Archivos de encabezado de ODBC