首页 > 其他分享 >ODBC客户端查询处理

ODBC客户端查询处理

时间:2024-01-24 23:00:27浏览次数:31  
标签:res 句柄 查询处理 ODBC stmt SQL NULL 客户端

本节,我将介绍一种新的句柄类型——SQLHSTMT语句句柄。SQLHSTMT的父级是连接句柄。您必须先释放所有子语句句柄,然后才能释放连接句柄。ODBC 结果处理模型比其他 PostgreSQL API 更复杂。在 libpq、libpq++ 和 libpgeasy API 中,您向服务器发送查询,然后调用函数来访问结果集中的每个字段(每行中)。

ODBC 应用程序通常使用不同的方案。将查询发送到服务器后,将结果集中的每个字段绑定到应用程序中的变量。绑定所有结果字段后,您可以获取结果集中的各个行,每次获取新行时,绑定变量都会由 ODBC 填充。

示例3 展示了如何执行查询并显示结果

#include <sql.h>
#include <sqlext.h>
#include <sqltypes.h>
#include <stdio.h>
#include <stdlib.h>
typedef enum {FALSE, TRUE} bool;
static bool SQL_OK(SQLRETURN result){
if(result==SQL_SUCCESS || result==SQL_SUCCESS_WITH_INFO)
	return(TRUE);
else
	return(FALSE);
}

typedef struct{
	char          name[128+1];
	SQLSMALLINT   nameLength;
	SQLSMALLINT   dataType;
	SQLULEN   	  fieldLength;
	SQLSMALLINT   scale;
	SQLSMALLINT   nullable;
	SQLLEN    	  displaySize;
	int           headerLength;
	SQLLEN        resultLength;
	char         *value;
} resultField;
static void printResultSet(SQLHSTMT stmt);

这里唯一的新内容是resultField结构体。我将使用resultField数组来处理结果集。这里有一个关于术语的注释:PostgreSQL 文档对字段和列进行了细微的区分。列指数据库中的列,而字段可以指列或计算值。ODBC 不做这种区分。

static bool printErrors(SQLHENV  	envHandle,
						SQLHDBC  	conHandle,
						SQLHSTMT 	stmtHandle){
	SQLRETURN   result;
	SQLCHAR     sqlState[6];
	SQLINTEGER  nativeError;
	SQLSMALLINT requiredLength;
	SQLCHAR     messageText[SQL_MAX_MESSAGE_LENGTH+1];
	do{
		result = SQLError(envHandle,
						  conHandle,
						  stmtHandle,
						  sqlState,
						  &nativeError,
						  messageText,
						  sizeof(messageText),
						  &requiredLength);
		if(SQL_OK(result)){
			printf("SQLState = %s\n", sqlState);
			printf("Native error = %d\n", nativeError);
			printf("Message text = %s\n", messageText);
		}
	} while(SQL_OK(result));
}

您已经在前面的示例中看到了SQL_OK()printErrors(),因此我不会在这里费心解释它们。

static void executeStmt(SQLHDBC con, char *stmtText){
	SQLHSTMT stmt;
	SQLAllocHandle(SQL_HANDLE_STMT, con, &stmt);
	if(SQL_OK(SQLExecDirect(stmt, stmtText, SQL_NTS)))
		printResultSet(stmt);
	else
		printErrors(SQL_NULL_HENV, SQL_NULL_HDBC, stmt);
}

executeStmt ()函数负责向服务器发送查询。首先分配一种新类型的句柄SQLHSTMTSQLHSTMT是一个语句句柄。语句句柄的父级始终是连接句柄(或 SQLHDBC )。

获得语句句柄后,使用SQLExecDirect()将查询发送到服务器。SQLExecDirect()非常简单,您提供一个语句句柄、要发送到服务器的查询文本以及查询字符串的长度(或SQL_NTS来指示查询文本是一个以 null 结尾的字符串) 。

如果SQLExecDirect()返回成功值,则调用printResultSet()来处理结果集。

static void printResultSet(SQLHSTMT stmt){
	SQLSMALLINT   i;
	SQLSMALLINT   columnCount;
	resultField  *fields;
	//首先,检查元数据,以便我们知道有多少结果集,我们拥有的字段和每个字段需要多少空间
	SQLNumResultCols(stmt, &columnCount);
	fields = (resultField *)calloc(columnCount+1, sizeof(resultField));
	for(i=1;i<=columnCount;i++){
		SQLDescribeCol(stmt,
		i,
		fields[i].name,
		sizeof( fields[i].name),
		&fields[i].nameLength,
		&fields[i].dataType,
		&fields[i].fieldLength,
		&fields[i].scale,
		&fields[i].nullable);
		
		SQLColAttribute(stmt, i, SQL_DESC_DISPLAY_SIZE, NULL, 0, NULL, &fields[i].displaySize);
		fields[i].value = (char *)malloc(fields[i].displaySize+1);
		if(fields[i].nameLength > fields[i].displaySize)
			fields[i].headerLength = fields[i].nameLength;
		else
			fields[i].headerLength = fields[i].displaySize;
	}
	//现在打印出列标题
	for(i=1;i<=columnCount;i++){
		printf( "%-*s ", fields[i].headerLength, fields[i].name );
	}
	printf( "\n" );
	//现在获取并显示结果… 
	while(SQL_OK(SQLFetch(stmt))){
		for(i=1;i<=columnCount;i++){
			SQLRETURN result;
			result = SQLGetData(stmt,
								i,
								SQL_C_CHAR,
								fields[i].value,
								fields[i].displaySize,
								&fields[i].resultLength);
		if(fields[i].resultLength == SQL_NULL_DATA)
			printf("%-*s", fields[i].headerLength, "");
		else
			printf("%-*s", fields[i].headerLength, fields[i].value);
		}
		printf("\n");
	}
	for(i=1;i<=columnCount;i++)
		free(fields[i].value);
	free(fields);
}

printResultSet ()函数有些复杂。它首先构建一个resultField结构体数组来跟踪刚刚执行的查询的元数据。

首先调用SQLNumResultCols()来确定结果集中将出现多少个字段(或列)。在知道要处理多少个字段后,您可以分配一个resultField结构数组——每个字段一个结构(以及一个额外的结构以简化代码)。

接下来,您调用两个元数据函数,以便了解每个字段返回的信息类型。SQLDescribeCol()函数返回给定字段的列名、数据类型、二进制字段长度、小数位数(用于数字数据类型)以及可为空性。请注意,字段索引从1开始,而不是0。

SQLColAttribute() 函数返回给定列 ( i )的特定元数据属性。您将以空终止字符串的形式检索每个字段,因此您需要知道每个字段的最大显示长度。SQL_DESC_DISPLAY_SIZE 属性正是您所需要的。

SQLDescribeCol()SQLColAttribute()函数都返回与列相关的元数据。SQLDescribeCol()是一个方便的函数,它返回最常用的元数据属性。调用SQLDescribeCol()相当于

SQLColAttribute(stmt, column, SQL_DESC_NAME, ...);
SQLColAttribute(stmt, column, SQL_DESC_TYPE, ...);
SQLColAttribute(stmt, column, SQL_DESC_LENGTH, ...);
SQLColAttribute(stmt, column, SQL_DESC_SCALE, ...);
SQLColAttribute(stmt, column, SQL_DESC_NULLABLE, ...);

检索并存储列的元数据后,您可以分配一个足够大的缓冲区来包含以 null 结尾的字符串形式的列数据。您还可以计算标头长度。您希望在足够大的水平空间中打印每列,以容纳列名称或列内容(以较长者为准)。

打印出列标题(第 122 到 126 行)后,我们开始处理结果集的内容。SQLFetch() 函数将获取与给定SQLHSTMT关联的结果集中的下一行。当您耗尽结果集时,SQLFetch()将返回值SQL_NO_DATA

ODBC 元数据类型到目前为止,我们只查看了描述结果集的元数据。由于 ODBC 被设计为应用程序和后端数据库之间的可移植层,因此 ODBC 提供了一组丰富的元数据功能。首先,您可以使用 SQLDataSources()函数检索系统上定义的数据源列表。SQLDrivers()函数将检索已安装驱动程序的列表。连接到数据源后,可以通过调用SQLGetTypeInfo()检索支持的数据类型的列表。该函数将列表作为结果集返回?您可以使用SQLFetch()SQLGetData()来获取列表。您可以使用SQLFunctions()来确定给定驱动程序支持哪些 ODBC API 函数。PostgreSQL ODBC 驱动程序(当前)是 ODBC 2.5 驱动程序,不直接支持 ODBC 3.0 函数。PostgreSQL 驱动程序不支持一些 ODBC 2.5 函数(例如SQLProcedures()SQLProcedureColumns()SQLBrowseConnect( ))。您还可以询问驱动程序是否支持各种SQL语法功能。例如,如果调用SQLGetInfo(..., SQL_CREATE_TABLE, ...),则可以确定数据库的CREATE TABLE语句支持哪些CREATE TABLE子句。SQLGetInfo()函数还返回版本信息,如表6所示。

SQLGetInfo() InfoType Argument Return Information
SQL_DBMS_VER Database version (for example, PostgreSQL 7.1.3)
SQL_DM_VER Driver manager version
SQL_DRIVER_NAME Driver name
SQL_DRIVER_ODBC_VER ODBC version that driver conforms to
SQL_DRIVER_VER Driver version
SQL_SERVER_NAME Name of server

您可以使用SQLGetInfo(..., SQL_TXN_CAPABLE, ...)来了解数据库的事务处理功能。据我统计,SQLGetInfo()可以返回超过 150 条有关数据源的不同信息!

如果SQLFetch()成功,则可以使用SQLGetData()函数检索当前行中的每一列,该函数具有以下原型:

SQLRETURN SQLGetData(SQLHSTMT stmtHandle,
                     SQLUSMALLINT columnNumber,
                     SQLSMALLINT  desiredDataType,
                     SQLPOINTER   destination,
                     SQLINTEGER   destinationLength,
                     SQLINTEGER  *resultLength);

当您调用SQLGetData()时,您希望 ODBC 将数据放入fields[i].value缓冲区中,以便您传递该地址(和displaySize)。传入SQL_C_CHAR的所需数据类型告诉 ODBC 以空终止字符串的形式返回每列。SQLGetData()返回fields[i].resultLength中的实际字段长度?如果字段为NULL,您将返回值SQL_NULL_DATA

第 143-146 行打印每个字段(在fields[i].headerLength空间内左对齐)。

最后,通过释放值缓冲区和resultField数组来进行清理:

int main(int argc, char * argv[]){
	SQLRETURN   res;
	SQLHENV     henv;
	SQLHDBC     conn;
	SQLCHAR     fullConnectStr[SQL_MAX_OPTION_STRING_LENGTH];
	SQLSMALLINT requiredLength;
	// 1.申请环境句柄
	res = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv);
	if(SQL_OK(res)){
		// 2.设置环境属性
		res = SQLSetEnvAttr(henv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC2, 0);
		if(!SQL_OK(res)){
			printErrors(henv, SQL_NULL_HDBC, SQL_NULL_HSTMT);
			exit(-1);
		}
		// 3.申请连接句柄
		res = SQLAllocHandle(SQL_HANDLE_DBC, henv, &conn);
		if(!SQL_OK(res)){
			printErrors(henv, SQL_NULL_HDBC, SQL_NULL_HSTMT);
			exit(-2);
		}
		res = SQLConnect(conn,  			// connection handle
						argv[1], SQL_NTS,   // data source name
						argv[2], SQL_NTS,   // user name
						argv[3], SQL_NTS);  // password
		if(!SQL_OK(res)){
			printErrors(SQL_NULL_HENV, conn, SQL_NULL_HSTMT);
			exit(-3);
		}
		printf("connection ok\n");
		executeStmt(conn, argv[4]);
		res = SQLDisconnect(conn);
		if(!SQL_OK(res)){
			printErrors( SQL_NULL_HENV, conn, SQL_NULL_HSTMT );
			exit(-4);
		}
		res = SQLFreeHandle(SQL_HANDLE_DBC, conn);
		if(!SQL_OK(res)){
			printErrors(SQL_NULL_HENV, conn, SQL_NULL_HSTMT);
			exit(-5);
		}
		res = SQLFreeHandle(SQL_HANDLE_ENV, henv);
		if(!SQL_OK(res)){
			printErrors(henv, SQL_NULL_HDBC, SQL_NULL_HSTMT);
			exit(-6);
		}
	}
	exit(0);
}

如果要运行此程序,执行如下:

gcc -o client3 client3.c -I /home/postgres/odbc/include -L /home/postgres/odbc/lib -lodbc
./client3 PSQLODBC postgres postgresql@123 'select * from a'

此示例向您展示了在 ODBC 应用程序中执行查询和处理结果的最简单方法。

标签:res,句柄,查询处理,ODBC,stmt,SQL,NULL,客户端
From: https://www.cnblogs.com/jl1771/p/17986060

相关文章

  • odbc客户端连接到服务器
    让我们看一个示例代码client1.c。第一个客户端应用程序连接到数据库,然后退出。#include<sql.h>#include<sqlext.h>#include<stdio.h>#include<stdlib.h>intmain(intargc,char*argv[]){ SQLRETURNresult; SQLHENVhenv; SQLHDBChdbc; //1.申请......
  • 服务器上mysql安装 ,以及客户端Navicat连接
    1.官网下载mysql8.0https://dev.mysql.com/downloads/installer/ 2.安装mysql8.0参考https://blog.csdn.net/weixin_47406082/article/details/131867849?ops_request_misc=&request_id=&biz_id=102&utm_term=mysql%E6%9C%80%E6%96%B0%E7%89%88%E5%AE%89%E8%A3%85%E......
  • centos7环境部署psqlodbc
    1获取unixODBC和psqlodbc源码包打开https://github.com/lurcher/unixODBC/tags,以下载unixODBC-2.3.7.tar.gz为例打开https://www.postgresql.org/ftp/odbc/versions/src/,以下载psqlodbc-09.06.0500.tar.gz为例将下载好的软件包放在/home/postgres2编译安装unixODBC执行如......
  • 前端歌谣-第六十五课-express之服务端渲染和客户端渲染
    前言我是歌谣微信公众号关注前端小歌谣一起学习前端知识今天继续给大家讲解服务端渲染和客户端渲染静态资源的讲解案列index.html<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><metaname="viewport"content="width=device-width,init......
  • elasticsearchjava客户端
    elasticsearchjava客户端1.引用maven配置<dependency><groupId>org.elasticsearch.client</groupId><artifactId>elasticsearch-rest-high-level-client</artifactId><exclusions><exclusion>&l......
  • 使用Go进行HTTP客户端认证
    在Go语言中,HTTP客户端认证可以通过net/http包来实现。下面是一个简单的示例,展示如何使用Go进行HTTP客户端认证。首先,确保你已经安装了Go语言环境,并设置好了相关的环境变量。Go语言中的HTTP客户端认证主要涉及到设置请求头中的认证信息。以下是一个简单的示例代码,展示了如何使用Go发......
  • k8s_client-go 构建客户端的几种方式
    kubernetesclient-go构建客户端的几种方式packagecallk8simport( "context" "log" metav1"k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes" "k8s.io/client-go/rest" "k8s.io/client-go/t......
  • 比特币客户端&比特币回归测试网络
    比特币客户端&比特币回归测试网络实验概述区块链技术需要协调一个庞大的去中心化网络以实现功能复杂的分布式状态机副本,必然涉及频繁的指令交互。在此过程中,除了设计功能完备、高鲁棒性的客户端程序,作为构建和调试分布式系统的重要协议,RPC(远程过程调用)也是实现上述功能不可或缺......
  • 前端歌谣-第六十五课-express之服务端渲染和客户端渲染
    前言我是歌谣微信公众号关注前端小歌谣一起学习前端知识今天继续给大家讲解服务端渲染和客户端渲染静态资源的讲解案列index.html<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><metaname="viewport"content="width=device-width,init......
  • HHDESK端口转发监控服务获取客户端和数据库之间的交互信息
    1.用户痛点端口转发是一种网络技术,用于将外部网络请求转发到内部网络中的特定设备或服务。它允许通过公共网络访问内部网络中的资源,提供了灵活性和便利性。传统的端口转发方式是通过配置路由器的端口映射,但这需要具备网络知识和一定的技术操作,对于一般用户来说较为繁琐。而HHDESK......