客户端编程

C语言

在本节中,我们将演示如何编写一个简单的 C/C++ 程序来访问 UDB-TDS。该示例不会创建完整且安全的程序,但突出显示了您的程序与 UDB-TDS 一起使用所需的一些功能。

本节中的示例是使用 FreeTDS 库为 Linux 编写的,该库适用于大多数 Linux 发行版。

要开始使用,请安装并配置 FreeTDS 驱动程序:

[root@fedora ~]# dnf install freetds*

Last metadata expiration check: 0:02:52 ago on Fr 24 Sep 2021 11:11:43 CEST.

Dependencies resolved.

====================================================================================================================================================

 Package                Architecture          Version                  Repository            Size

====================================================================================================================================================

Installing:

 freetds                x86_64             1.1.20-4.fc34               fedora             373 k

 freetds-devel             x86_64             1.1.20-4.fc34               fedora              39 k

 freetds-doc              noarch             1.1.20-4.fc34               fedora             1.0 M

Installing dependencies:

 freetds-libs              x86_64             1.1.20-4.fc34               fedora             423 k

如果您使用 C 语言编码,请确保您还安装了包(头文件)。在系统上编译代码时会使用头文件。

连接到数据库

示例的第一部分管理头文件、变量定义和错误处理。该示例使用标准 C 库(stdio.h、stdlib.h、unistd.h、sys/param.h)和 FreeTDS 库(sybfront.h、sybdb.h 和 syberror.h)中的头文件:

#include <stdio.h>

#include <stdlib.h>

#include <unistd.h>

#include <sys/param.h>

#include <sybfront.h>

#include <sybdb.h>

#include <syberror.h>

#define  UID    "postgres"  

#define  PWD    "verysecret!"

#define  PROGNAME  "demo_prog"

#define  DBSERVER  "sample-host.example.com"

#define  DBNAME   "postgres"

 

/* handler for messages from the server */

static int

msg_handler(DBPROCESS* dbproc, DBINT msgno, int msgstate, int severity, 

 char *msgtext, char *srvname, char *procname, int line)

{

 /* regular errors are handled by the error handler */

 if (severity < 11)

     fprintf(stderr, "Server message (severity %d): %s\n", severity, msgtext);

 

 return 0;

}

 

/* error handler */

static int err_handler(DBPROCESS* dbproc, int severity, int dberr, int oserr, char *dberrstr, char *oserrstr)

{

 fprintf(stderr, "Server error %d: %s\n", dberr, dberrstr);

 if (oserr != 0)

    fprintf(stderr, "Caused by system error %d: %s\n", oserr, oserrstr);

 

 return INT_CANCEL;

}

该示例还在初始化 FreeTDS 库之前声明局部变量:

int main(void)

{

  LOGINREC  *login;

  DBPROCESS  *dbconn;

  char  hostname[MAXHOSTNAMELEN];

  int  max_len = MAXHOSTNAMELEN;

  DBCHAR  accession[10];

  DBCHAR  examdesc[10];

  DBCHAR  examcode[255];

 

  if (dbinit() == FAIL) 

 {

   fprintf(stderr, "Could not init db.\n");

   return 1;

  }

接下来,分配一个登录结构,并将登录 ID、密码和主机名设置到登录句柄中:

  /* Allocate a login params structure */ 

 if ((login = dblogin()) == FAIL) 

 {

 fprintf(stderr, "Could not initialize dblogin() structure.\n");

   return 2;

  }

 

 /* Initialize the login params in the structure */

 DBSETLUSER(login, UID);

 DBSETLPWD(login, PWD);

 

 if (gethostname(hostname, max_len) == 0)

 {

   DBSETLHOST(login, hostname);

 fprintf(stderr, "setting login hostname: %s\n", hostname);

 }  

设置 TDS 端口环境变量(如果在默认端口 1433 上进行连接,则不需要):

 /* the port can only be set via environment variable */

 

 if (putenv("TDSPORT=1433") != 0)

 {

     fprintf(stderr, "error setting TDSPORT environment variable\n");

     return 0;

 }

安装错误处理程序和消息处理程序 - 如果发生错误/消息,TDS 库将调用给定的函数。我们的示例将消息发送到:stderr

 /* install error handler */

 dberrhandle(err_handler);

 dbmsghandle(msg_handler);

调用以连接到服务器:dbopen

 /* Now connect to the DB Server */

 if ((dbconn = dbopen(login, DBSERVER)) == NULL) 

 {

 fprintf(stderr, "Could not connect to DB Server: %s\n", DBSERVER);

   return 3;

 }

 

 printf("success\n");

 return 0;

}

使用 GCC 编译代码:

gcc main_01.c -lsybdb -I/usr/include/ -o main_01

编译代码时,有两件事至关重要:

您应该包括 -I 选项,以告诉编译器在哪里查找头文件。

包括 -lsydbd 选项以确保 FreeTDS 库已正确链接。

此示例创建名为main_01 的二进制文件。

这是一个简单的例子,但它突出显示了在开始开发与 C 语言 UDB-TDS 服务器联系的程序时需要捕获的行为。

FreeTDS 配置问题

以下消息是由 FreeTDS 配置错误引起的:

[hs@fedora tds_test]$ ./main_01 

setting login hostname: fedora

Server error 20018: General SQL Server error: Check messages from the SQL Server

Caused by system error -1: (null)

Server error 20002: Adaptive Server connection failed (sample-host.example.com)

Server error 20002: Adaptive Server connection failed (sample-host.example.com)

Could not connect to DB Server: sample-host.example.com)

如果您收到此错误消息,则 FreeTDS 配置文件可能包含错误或不存在。

C#语言

Microsoft SQL Server是Microsoft Windows平台上的主要数据库系统之一,C#是该生态系统中使用的较流行的语言之一。因此,了解如何建立 C# 连接以及如何获取数据非常重要。

C#语言示例代码

让我们看一下示例代码:

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Threading.Tasks;

using System.Data.SqlClient;

 

namespace sample

{

  class Program

  {

    static void Main(string[] args)

    {

      // Setting up MSSQL Credentials

      SqlConnection con;

 

      string conString = "Server=" + @"PUT_HOSTNAME_HERE" + ";" +

                "User id=" + "PUT_USERNAME_HERE" + ";" +

                "Password=" + "PUT_PASSWORD_HERE" + ";" +

                "Database=" + "PUT_DATABASE_HERE" + ";" +

                "MultipleActiveResultSets=true;";

 

      con = new SqlConnection(conString);

      SqlCommand cmd = new SqlCommand();

 
      // Creating MSSQL Connection

      try

      {

        con.Open();

        Console.WriteLine("Connection established\n") ;

      }

      catch

      {

        Console.WriteLine("Can not connect to database!\nPlease check credentials!");

        Environment.Exit(1);

      }

 

      string sqlQuery = "";

 

      // Select values example

      select_all(con);

 

      // Transaction example

      // Insert values into sample table

      cmd = con.CreateCommand();

      SqlTransaction transaction = con.BeginTransaction("SampleTransaction");

 

      try

      {

        sqlQuery = "INSERT INTO sample VALUES(@vorname, @nachname, @persoid)";

        cmd.Parameters.AddWithValue("@vorname", "Max");

        cmd.Parameters.AddWithValue("@nachname", "Mustermann");

        cmd.Parameters.AddWithValue("@persoid", "1020");

        cmd.CommandType = System.Data.CommandType.Text;

        cmd.CommandText = sqlQuery;

        cmd.Transaction = transaction;

 

        cmd.ExecuteNonQuery();

 

        cmd.Parameters.Clear();

        cmd.Parameters.AddWithValue("@vorname", "Erika");

        cmd.Parameters.AddWithValue("@nachname", "Musterfrau");

        cmd.Parameters.AddWithValue("@persoid", "1021");

 

        cmd.ExecuteNonQuery();

 

        transaction.Commit();

 

        Console.WriteLine("\nInsert successful!\n");

      }

      catch

      {

        transaction.Rollback();

        Console.WriteLine("\nInsert failed!\n");

      }

 

      select_all(con);

 

      // Removing inserted values

      sqlQuery = "DELETE FROM sample WHERE vorname = 'Max' or vorname = 'Erika'";

      cmd = con.CreateCommand();

      cmd.CommandText = sqlQuery;

 

      int row_count = cmd.ExecuteNonQuery();

 

      // Select metadata

      // Select row count from delete

      Console.WriteLine("\nDeleted rows: " + row_count + "\n");

 

      // Select column names from table

      sqlQuery = "SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'sample'";

      cmd = con.CreateCommand();

      cmd.CommandText = sqlQuery;

 

      SqlDataReader reader = cmd.ExecuteReader();

 

      string value = "";

 

      while (reader.Read())

      {

        value += reader.GetValue(0) + " ";

      }

 

      Console.WriteLine(value);

      reader.Close();

 

      // Closing connection

      con.Close();

      Console.WriteLine("\nConnection closed!");

    }

 

    private static void select_all(SqlConnection con)

    {

      string sqlQuery = "SELECT * FROM sample";

      SqlCommand cmd = con.CreateCommand();

      cmd.CommandText = sqlQuery;

      SqlDataReader reader = cmd.ExecuteReader();

 

      while (reader.Read())

      {

        string value = "";

        for (int i = 0; i != reader.FieldCount; i++)

        {

          value += reader.GetValue(i) + " ";

        }

        Console.WriteLine(value);

      }

 

      reader.Close();

	}

 

  }

}

代码相对简单。首先,分配并打开连接。在 C# 中,可以使用 try / catch 轻松完成错误处理。在我们的例子中,如果无法打开连接,程序就会终止。

然后我们调用 select_all 函数。它的作用是创建一个 SQL 命令,该命令只需读取“示例”表中的所有数据,并循环遍历所有行和所有列以在屏幕上显示它们。最后,SqlDataReader 关闭。这是一个简单的例子,展示了如何轻松地从UDB-TDS中提取数据。

接下来是一个示例,演示如何运行事务。执行此操作的方法是运行 BeginTransaction 方法。运行 SQL 后,我们可以提交事务。错误处理由 try / catch 块完成。

可以使用 Microsoft Studio 或您选择的任何其他工具编译代码。需要注意的重要一点是,您可以像通常实现代码一样实现 UDB-TDS 的代码,直接与 Microsoft SQL Server 通信。

Python语言

将 Python 与 UDB-TDS 结合使用

在本节中,您将学习如何使用 Python 连接到 UDB-TDS 并从服务器中提取数据。将显示三种变体:

使用 Python 和 ODBC

使用 Python 和本机 TDS

将 Python 与 FreeTDS 配合使用(在 arm64 体系结构上不存在)msobdcsql

所有这三个变体都包含一些示例,这些示例将帮助您在 UDB-TDS 中处理事务操作。

使用 Python 和 ODBC

在运行 Python 代码之前,您需要确保所有库都存在。如果您使用的是 Ubuntu 20.04,则可以使用以下命令来更新任何丢失的包:

#!/bin/bash

sudo su

curl https://packages.microsoft.com/keys/microsoft.asc | apt-key add -

curl https://packages.microsoft.com/config/ubuntu/20.04/prod.list > /etc/apt/sources.list.d/mssql-release.list

exit

sudo apt-get update

sudo ACCEPT_EULA=Y apt-get install -y msodbcsql17

根据您的 Linux 发行版,安装过程会略有不同,但基本要求是相同的 - 确保已安装库。

在调用示例代码之前,还需要创建一个名为 的 UDB-TDS 表。您可以在 UDB-TDS 数据库的 TDS 端口上使用您选择的客户端进行连接,并使用以下命令:contacts

CREATE TABLE contacts (

  contact_id INT PRIMARY KEY,

  first_name VARCHAR (10) NOT NULL,

  last_name VARCHAR (20) NOT NULL,

  visited DATE

);

 

INSERT INTO contacts VALUES ('1', 'Alex', 'Arthur', '20210831');

INSERT INTO contacts VALUES ('2', 'Bonnie', 'Bret', '20210831');

INSERT INTO contacts VALUES ('3', 'Colin', 'Cristobal', '20210901');

INSERT INTO contacts VALUES ('4', 'Danielle', 'Dexter', '20210907');

INSERT INTO contacts VALUES ('5', 'Earl', 'Edwards', '20210908');

INSERT INTO contacts VALUES ('6', 'Fiona', 'Ferdinand', '20210917');

我们的示例侧重于使用 Python、ODBC 和 UDB-TDS 创建连接、读取数据并将其显示在屏幕上。首先,该示例包括它将使用的库:

import sys

import os

import pyodbc

然后,该示例创建与 SQL Server 主机的连接。连接字符串的语法必须包含正确格式的所有相关信息:

# Provide your SQL Server credentials

server = 'host.example.com'

database = 'postgres'

username = 'postgres'

password = '1safepassword'

 

\# Establish a connection

try:

  connection = pyodbc.connect('DRIVER={ODBC Driver 17 for SQL Server};SERVER='+server+';DATABASE='+database+';UID='+username+';PWD='+ password)

  cursor = connection.cursor()

  print("Connection established for select examples\n")

except pyodbc.ProgrammingError:

  print("Cannot connect to the database\nPlease check credentials")

  exit(1)

Then, the example creates a cursor that iterates through the rows of your contacts table and displays them:

 

\# Select values

cursor.execute("SELECT * FROM contacts")

 

for row in cursor.fetchall():

  print(row)

The example will display:

 

Connection established for select examples

 

(1, 'Alex', 'Arthur', 20210831)

(2, 'Bonnie', 'Bret', 20210831)

(3, 'Colin', 'Cristobal', 20210901)

(4, 'Danielle', 'Dexter', 20210907)

(5, 'Earl', 'Edwards', 20210908)

(6, 'Fiona', 'Ferdinand', 20210917)

 

Transaction handling

Our next example demonstrates using cursors and native TDS to perform some basic transaction handling. First, the example establishes a connection with the server:

 

\# Transaction example

\# Establish a UDBTX-TDS connection

try:

  transact_connection = pyodbc.connect('DRIVER={ODBC Driver 17 for SQL Server};SERVER='+server+';DATABASE='+database+';UID='+username+';PWD='+ password, autocommit=False)

  t_cursor = transact_connection.cursor()

  print("\nConnection established\n")

except pyodbc.ProgrammingError:

  print("\nConnection attempt failed\nPlease check credentials")

  exit(1)

Then, the example uses a cursor to add new entries to the database:

 

\# Insert values into your contacts table

try:

  t_cursor.execute("INSERT INTO contacts VALUES('Gaston', 'Gordon', '20210918')")

  t_cursor.execute("INSERT INTO contacts VALUES('Hermione', 'Henry', '20210925')")

except pyodbc.Error as e:

  t_cursor.rollback()

  print("Insert failed: {}".format(e))

else:

  t_cursor.commit()

  print("Insert successful\n")

Then, the example iterates through the table and display the contents:

 

t_cursor.execute("SELECT * FROM contacts")

 

for row in t_cursor.fetchall():

  print(row)

 

t_cursor.close()

transact_connection.close()

The example then removes two rows from the table:

 

\# Remove inserted values

cursor.execute("DELETE FROM contacts WHERE last_name = 'Bret' OR last_name = 'Edwards'")

cursor.commit()

 

\# Select metadata

print(f"\nDeleted Rows: {cursor.rowcount}\n")

Then, the example closes the connection and alerts the user:

 

\# Close connection

cursor.close()

connection.close()

print("Success")

The example should display the following messages:

 

Connection established

 

Insert successful

 

(1, 'Alex', 'Arthur', 20210831)

(3, 'Colin', 'Cristobal', 20210901)

(4, 'Danielle', 'Dexter', 20210907)

(6, 'Fiona', 'Ferdinand', 20210917)

(7, 'Gaston', 'Gordon', 20210918)

(8, 'Hermione', 'Henry', 20210925)

 

Deleted Rows: 2

 

Success

这个表现在反映了在示例中所做的更改。

用Python和FreeTDS

如果您使用的是arm64体系结构,则可以使用FreeTDS驱动程序通过TDS协议进行连接。

在调用样例代码之前,使用以下命令安装必备软件:

sudo apt install python3-pip python3-venv freetds-dev

python3 -m venv .venv

source .venv/bin/activate

pip install pymssql psycopg2

该示例使用pymssql API将数据写入UDB-TDS,并使用psycopg2库从UDB-TDS读取数据。该示例包括在与UDB-TDS主机建立连接之前使用这些库所需的库:

import sys

import os

import pymssql

import psycopg2

 

server = 'host.example.com'

port = 1433

database = 'master'  

username = 'UDBTX-TDS_user'

password = '1safepassword'

 

pg_database = "UDBTX-TDS_db"

pg_schema  = "master_dbo"

 

def main():

 

  with pymssql.connect(server, username, password, database) as conn:

    with conn.cursor() as cursor:

Then, the example drops and recreates a simple table (named ) and a procedure (named ):contactsfind_contact

 

      try:

        cursor.execute("DROP TABLE contacts")

        cursor.execute("DROP PROCEDURE find_contact")

        conn.commit()

      except Exception as e:

        \# For the sake of this example, we 

        \# ignore exceptions if the objects do not exist.

        pass

      

      try:

        print("-- create table: ")

        cursor.execute("""

          CREATE TABLE contacts (

            contact_id INT PRIMARY KEY,

            first_name VARCHAR (10) NOT NULL,

            last_name VARCHAR (20) NOT NULL,

            visited DATE

          )

        """)

        cursor.execute("""

          CREATE PROCEDURE find_contact

            @last_name VARCHAR(100)

          AS BEGIN

            SELECT * FROM contacts WHERE last_name like '%'+@last_name+'%'

          END

        """)

        conn.commit()

      except Exception as e:

        print("Cannot create table: {}".format(e))

        exit(1)

给contacts表插入数据:

      try:

        print("-- Insert")

        cursor.executemany(

          "INSERT INTO contacts VALUES (%d, %s, %s, %s)",

          [

            (11, 'Imelda', 'Imani', '20211004'),

            (12, 'Julian', 'Joyce', '20211015'),

            (13, 'Katia', 'Kirk', '20211027')

          ])

        conn.commit()

      except Exception as e:

        print("Transaction could not be committed: {} ".format(e))

        exit(2) # Duplicate key error

然后,该示例首先使用库查询表,然后使用 API:psycopg2pymmssql

  with psycopg2.connect(user=username, password=password, host=server,database=pg_database) as connpg:

    with connpg.cursor() as cursorpg:

      print("-- Output from UDBTX-TDS: ")

      try:

        cursorpg.execute("SELECT * FROM {}.contacts WHERE last_name like '%{}%'".format(pg_schema,"Joyce"))

        for row in cursorpg:

          print("{}".format(row))

      except Exception as e:

        print("Read query couldn't be executed: {}".format(e))

        pass

  

  with pymssql.connect(server, username, password, database) as conn:

    with conn.cursor() as cursor:

      print("-- Executing PROCEDURE ")

      cursor.callproc('find_contact', ('Joyce',))

      for row in cursor:

        print("{}".format(row))

该子句允许示例在块完成后清理游标和连接,以便更干净地执行。with

最后,将 init 调用添加到函数中:main()

if name == “main”:

main()

JAVA语言

利用 JDBC

在关注了其他一些流行的编程语言(如 Python 和 C#)之后,我们还想坐下来弄清楚如何将 UDB-TDS 与 Java 和 JDBC 结合使用。同样,将展示一些基本操作来描述如何编写代码。

正如我们之前所看到的,为 Microsoft SQL Server 和 UDB-TDS 编写 JDBC 客户端代码之间没有真正的区别。不过,让我们深入研究并看一些例子:

使用 JDBC 的示例代码

要编译 Java 代码,您将需要 Microsoft SQL Server JDBC 驱动程序。为了这个例子,我们使用了9.4(mssql-jdbc-9.4.0.jre11.jar)版本。但是,由于该示例具有非常基本的代码,因此也应该可以使用其他版本的驱动程序运行内容。

代码如下:

package sample;

import java.sql.Connection;

import java.sql.DriverManager;

import java.sql.ResultSet;

import java.sql.SQLException;

import java.sql.Statement;

 

public class sample {

 public static void main(String[] args) {

 Connection connection;

 // Setting up MSSQL Credentials

 String connectionUrl =

  "jdbc:sqlserver://PUT_HOSTNAME_HERE;"

  \+ "database=PUT_NAME_OF_DATABASE_HERE;"

  \+ "user=PUT_USERNAME_HERE;"

  \+ "password=PUT_PASSWORD_HERE;";

 

 try {

  // Trying to establish connection

  connection = DriverManager.getConnection(connectionUrl);

  System.out.println("Connection established for select examples!\n");

  

  // Select values example

  select_all(connection);

  

  // Transaction example

  connection.setAutoCommit(false);

  

  // Insert values into sample table

  try {

  connection.setAutoCommit(false);

  

  String insert = "INSERT INTO sample VALUES('Max', 'Mustermann', '1020')";

  Statement statement = connection.createStatement();

  statement.execute(insert);

  

  insert = "INSERT INTO sample VALUES('Erika', 'Musterfrau', '1021')";

  statement.execute(insert);

  

  connection.commit();

  System.out.println("\nInsert successful!\n");

  }

  catch (SQLException e){

  connection.rollback();

  System.out.println("\nInsert failed!\n");

  }

  

  select_all(connection);

  

  connection.setAutoCommit(true);

  

  // Removing inserted values and displaying rowcount

  try {

  String delete = "DELETE FROM sample WHERE vorname = 'Max' or vorname = 'Erika'";

  Statement statement = connection.createStatement();

  int rowcount = statement.executeUpdate(delete);

  

  System.out.println("\nDeleted Rows: " + rowcount + "\n");

  

  // Select column names

  String select = "SELECT * FROM sample";

  ResultSet result = statement.executeQuery(select);

  

  String value = "";

  

  for(int i = 1; i <= result.getMetaData().getColumnCount(); i++) {

   value += result.getMetaData().getColumnName(i) + " ";

  }

  

  System.out.println(value);

  }

  catch (SQLException e) {

  e.printStackTrace();

  }

 }

 catch (SQLException e) {

  e.printStackTrace();

 }

 }

 

 private static void select_all(Connection connection) {

 try {

  Statement statement = connection.createStatement();

  

  String select = "SELECT * FROM sample";

  ResultSet result = statement.executeQuery(select);

  

  while(result.next()) {

  String value = "";

  for(int i = 1; i <= result.getMetaData().getColumnCount(); i++) {

   value += result.getString(i) + " ";

  }

  System.out.println(value);

  }

 }

 catch (SQLException e) {

  e.printStackTrace();

 }

 }

}

首先,创建一个标准的JDBC连接。JDBC是一个抽象层,所以主要的魔力实际上在连接字符串中:“jdbc:sqlserver”将告诉我们的程序使用Microsoft SQL Server驱动程序连接到UDB-TDS。

否则,示例代码大多是微不足道的,几乎代表了一个标准的 JDBC 应用程序。

支持的连接驱动程序

支持以下接口:

OLEDB Provider/MSOLEDBSQL

OLEDB Driver/SQLOLEDB (deprecated by Microsoft)

.NET Data Provider for SQL Server

SQL Server Native Client 11.0 (deprecated by Microsoft)

Microsoft SqlClient Data Provider for SQL Server

Open Database Connectivity (ODBC)

Java Database Connectivity (JDBC)

将来可能会添加更多连接驱动程序。由于 UDB-TDS 支持 TDS 协议,因此大多数基于 TDS 的客户端应用程序都应与 UDB-TDS 一起使用。