ODBC
Данная реализация тестировалась с менеджерами драйверов unixODBC и iODBC2,
с драйверами для MySQL и Postgres.
Чтобы построить приложение с использованием ODBC
его нужно слинковать с библиотекой libclip-odbc.so. Например:
clip -e test.prg -lclip-odbc
Для подключения к серверу используйте конструктор класса TConnect
в следующем синтаксисе:
ConnectNew("ODBC",<DSN>,[<login>],[<pwd>]), где
"ODBC" - идентификатор СУБД;
<DSN> - Data Source Name (источник данных. См. файл odbc.ini);
<login> - логин пользователя;
<pwd> - пароль пользователя.
Реализация ODBC для Clip, в отличие от реализаций других СУБД,
имеет ряд ограничений по использованию. Например, поскольку ODBC
не имеет стандартного механизма для выяснения значений ROWID вновь
вставленной записи, вы не сможете изменять вновь вставленные записи
путем изменения записи TRowset. Также, поскольку под руками не было
какого-нибудь драйвера и СУБД для тестирования, механизм передачи
параметров SQL-запросу лишь эмулируется (как это сделано в реализациях
для MySQL и Postgres).
Для связи записей набора (TRowset) и физической таблицы базы данных
испольуется поле, указываемое в дополнительном, 6-ом параметре
функции CreateRowset(). Для полноценной работы с набором (с возможностью
отражения производимых изменений набора записей в базе данных) в список
запрашиваемых полей запроса SELECT следует явно включать зто поле.
В операторах автоматического отражения (deleteSQL и updateSQL, передаваемых
конструктору TRowset) следует использовать предложение типа WHERE id=:id, где
id - имя идентифицирующего поля (например, ROWID в Oracle, OID в Postgres).
Например:
DELETE FROM mytable WHERE id=:id
UPDATE mytable SET fname=:fname,lname=:lname WHERE id=:id
Поскольку интерфейс ODBC не предоставляет соответствующей возможности,
уровень изоляции необходимо выставлять средствами используемого сервера SQL.
Параметр <cIsolation> функций ConnectNew() и TConnect:Start()
игнорируется.
В качестве простейшего примера использования clip-odbc, см. утилиту
my_isql, которая представляет собой простой фронтенд, аналогичный isql.
Вот ее текст:
LOCAL conn,rs,data
LOCAL sql := ""
LOCAL I := 1
LOCAL user // -u
LOCAL passwd // -p
LOCAL db
ErrorBlock({|e| SQLError(e)})
DO WHILE param(I) != NIL
IF param(I) == "--help" .OR. param(I) == "-?"
Usage()
QUIT
ELSEIF param(I) == "-h"
host := param(++I)
ELSEIF param(I) == "-u"
user := param(++I)
ELSEIF param(I) == "-p"
passwd := param(++I)
ELSE
db := param(I)
ENDIF
I++
ENDDO
IF EMPTY(db)
Usage()
QUIT
ENDIF
CLS
BEGIN SEQUENCE
conn := ConnectNew("ODBC",db,user,passwd)
RECOVER USING e
QUIT
END SEQUENCE
DO WHILE UPPER(sql := GetCommand()) != "Q"
IF UPPER(LEFT(sql,6))=="SELECT"
BEGIN SEQUENCE
rs := conn:CreateRowset(sql)
RECOVER USING e
LOOP
END SEQUENCE
FOR I:=1 TO rs:NFields()
?? PADR(rs:FieldName(I),10),"|"
NEXT
?
FOR I:=1 TO rs:NFields()
?? "----------","|"
NEXT
DO WHILE !rs:Eof()
?
data := rs:Read()
FOR I:=1 TO rs:NFields()
tmp := data[HASHSTR(UPPER(rs:FieldName(I)))]
IF VALTYPE(tmp)=="A"
?? "{...} ","|"
ELSEIF tmp==NIL
?? "NIL ","|"
ELSE
?? PADR(data[HASHSTR(UPPER(rs:FieldName(I)))],10),"|"
ENDIF
NEXT
rs:Skip()
ENDDO
rs:Destroy()
ELSE
BEGIN SEQUENCE
?? LEFT(sql,LEN(sql)-1)+": "
conn:Command(sql)
?? "OK"
RECOVER USING e
LOOP
END SEQUENCE
ENDIF
?
ENDDO
conn:Destroy()
RETURN
FUNCTION GetCommand()
LOCAL ret := ""
LOCAL tmp := ""
LOCAL first := .T.
@ MAXROW(),0 SAY ""
DO WHILE !(";" $ tmp) .AND. ALLTRIM(UPPER(ret)) != "Q"
IF first
ACCEPT "SQL> " TO tmp
first := .F.
ELSE
ACCEPT "SQL---> " TO tmp
ENDIF
ret += tmp + " "
ENDDO
RETURN ALLTRIM(ret)
FUNCTION SQLError(e)
?? e:operation
?
BREAK(e)
RETURN NIL
PROCEDURE Usage()
?? "my_isql Ver 1.00 (simple ODBC frontend) (c) by ITK Ltd., by Rusty"
? "This software comes with ABSOLUTELY NO WARRANTY."
?
? "Usage: my_isql [OPTIONS] "
?
? " -?, --help Display this help and exit"
? " -u User for login if not current user"
? " -p Password to use when connecting to server"
?
RETURN