! Copyright (C) 2007 Chris Double. ! See http://factorcode.org/license.txt for BSD license. USING: kernel alien alien.syntax combinators alien.c-types strings sequences namespaces words math threads ; IN: odbc "odbc" "odbc32.dll" "stdcall" add-library LIBRARY: odbc TYPEDEF: void* usb_dev_handle* TYPEDEF: short SQLRETURN TYPEDEF: short SQLSMALLINT TYPEDEF: short* SQLSMALLINT* TYPEDEF: ushort SQLUSMALLINT TYPEDEF: uint* SQLUINTEGER* TYPEDEF: int SQLINTEGER TYPEDEF: char SQLCHAR TYPEDEF: char* SQLCHAR* TYPEDEF: void* SQLHANDLE TYPEDEF: void* SQLHANDLE* TYPEDEF: void* SQLHENV TYPEDEF: void* SQLHDBC TYPEDEF: void* SQLHSTMT TYPEDEF: void* SQLHWND TYPEDEF: void* SQLPOINTER : SQL-HANDLE-ENV ( -- number ) 1 ; inline : SQL-HANDLE-DBC ( -- number ) 2 ; inline : SQL-HANDLE-STMT ( -- number ) 3 ; inline : SQL-HANDLE-DESC ( -- number ) 4 ; inline : SQL-NULL-HANDLE ( -- alien ) f ; inline : SQL-ATTR-ODBC-VERSION ( -- number ) 200 ; inline : SQL-OV-ODBC2 ( -- number ) 2 ; inline : SQL-OV-ODBC3 ( -- number ) 3 ; inline : SQL-SUCCESS ( -- number ) 0 ; inline : SQL-SUCCESS-WITH-INFO ( -- number ) 1 ; inline : SQL-NO-DATA-FOUND ( -- number ) 100 ; inline : SQL-DRIVER-NOPROMPT ( -- number ) 0 ; inline : SQL-DRIVER-PROMPT ( -- number ) 2 ; inline : SQL-C-DEFAULT ( -- number ) 99 ; inline SYMBOL: SQL-CHAR SYMBOL: SQL-VARCHAR SYMBOL: SQL-LONGVARCHAR SYMBOL: SQL-WCHAR SYMBOL: SQL-WCHARVAR SYMBOL: SQL-WLONGCHARVAR SYMBOL: SQL-DECIMAL SYMBOL: SQL-SMALLINT SYMBOL: SQL-NUMERIC SYMBOL: SQL-INTEGER SYMBOL: SQL-REAL SYMBOL: SQL-FLOAT SYMBOL: SQL-DOUBLE SYMBOL: SQL-BIT SYMBOL: SQL-TINYINT SYMBOL: SQL-BIGINT SYMBOL: SQL-BINARY SYMBOL: SQL-VARBINARY SYMBOL: SQL-LONGVARBINARY SYMBOL: SQL-TYPE-DATE SYMBOL: SQL-TYPE-TIME SYMBOL: SQL-TYPE-TIMESTAMP SYMBOL: SQL-TYPE-UTCDATETIME SYMBOL: SQL-TYPE-UTCTIME SYMBOL: SQL-INTERVAL-MONTH SYMBOL: SQL-INTERVAL-YEAR SYMBOL: SQL-INTERVAL-YEAR-TO-MONTH SYMBOL: SQL-INTERVAL-DAY SYMBOL: SQL-INTERVAL-HOUR SYMBOL: SQL-INTERVAL-MINUTE SYMBOL: SQL-INTERVAL-SECOND SYMBOL: SQL-INTERVAL-DAY-TO-HOUR SYMBOL: SQL-INTERVAL-DAY-TO-MINUTE SYMBOL: SQL-INTERVAL-DAY-TO-SECOND SYMBOL: SQL-INTERVAL-HOUR-TO-MINUTE SYMBOL: SQL-INTERVAL-HOUR-TO-SECOND SYMBOL: SQL-INTERVAL-MINUTE-TO-SECOND SYMBOL: SQL-GUID SYMBOL: SQL-TYPE-UNKNOWN : convert-sql-type ( number -- symbol ) { { [ dup 1 = ] [ drop SQL-CHAR ] } { [ dup 12 = ] [ drop SQL-VARCHAR ] } { [ dup -1 = ] [ drop SQL-LONGVARCHAR ] } { [ dup -8 = ] [ drop SQL-WCHAR ] } { [ dup -9 = ] [ drop SQL-WCHARVAR ] } { [ dup -10 = ] [ drop SQL-WLONGCHARVAR ] } { [ dup 3 = ] [ drop SQL-DECIMAL ] } { [ dup 5 = ] [ drop SQL-SMALLINT ] } { [ dup 2 = ] [ drop SQL-NUMERIC ] } { [ dup 4 = ] [ drop SQL-INTEGER ] } { [ dup 7 = ] [ drop SQL-REAL ] } { [ dup 6 = ] [ drop SQL-FLOAT ] } { [ dup 8 = ] [ drop SQL-DOUBLE ] } { [ dup -7 = ] [ drop SQL-BIT ] } { [ dup -6 = ] [ drop SQL-TINYINT ] } { [ dup -5 = ] [ drop SQL-BIGINT ] } { [ dup -2 = ] [ drop SQL-BINARY ] } { [ dup -3 = ] [ drop SQL-VARBINARY ] } { [ dup -4 = ] [ drop SQL-LONGVARBINARY ] } { [ dup 91 = ] [ drop SQL-TYPE-DATE ] } { [ dup 92 = ] [ drop SQL-TYPE-TIME ] } { [ dup 93 = ] [ drop SQL-TYPE-TIMESTAMP ] } { [ t ] [ drop SQL-TYPE-UNKNOWN ] } } cond ; : succeeded? ( n -- bool ) #! Did the call succeed (SQL-SUCCESS or SQL-SUCCESS-WITH-INFO) { { [ dup SQL-SUCCESS = ] [ drop t ] } { [ dup SQL-SUCCESS-WITH-INFO = ] [ drop t ] } { [ t ] [ drop f ] } } cond ; FUNCTION: SQLRETURN SQLAllocHandle ( SQLSMALLINT handleType, SQLHANDLE inputHandle, SQLHANDLE* outputHandlePtr ) ; FUNCTION: SQLRETURN SQLSetEnvAttr ( SQLHENV environmentHandle, SQLINTEGER attribute, SQLPOINTER valuePtr, SQLINTEGER stringLength ) ; FUNCTION: SQLRETURN SQLDriverConnect ( SQLHDBC connectionHandle, SQLHWND windowHandle, SQLCHAR* inConnectionString, SQLSMALLINT stringLength, SQLCHAR* outConnectionString, SQLSMALLINT bufferLength, SQLSMALLINT* stringLength2Ptr, SQLUSMALLINT driverCompletion ) ; FUNCTION: SQLRETURN SQLDisconnect ( SQLHDBC connectionHandle ) ; FUNCTION: SQLRETURN SQLPrepare ( SQLHSTMT statementHandle, SQLCHAR* statementText, SQLINTEGER length ) ; FUNCTION: SQLRETURN SQLExecute ( SQLHSTMT statementHandle ) ; FUNCTION: SQLRETURN SQLFreeHandle ( SQLSMALLINT handleType, SQLHANDLE handle ) ; FUNCTION: SQLRETURN SQLFetch ( SQLHSTMT statementHandle ) ; FUNCTION: SQLRETURN SQLNumResultCols ( SQLHSTMT statementHandle, SQLSMALLINT* columnCountPtr ) ; FUNCTION: SQLRETURN SQLDescribeCol ( SQLHSTMT statementHandle, SQLSMALLINT columnNumber, SQLCHAR* columnName, SQLSMALLINT bufferLength, SQLSMALLINT* nameLengthPtr, SQLSMALLINT* dataTypePtr, SQLUINTEGER* columnSizePtr, SQLSMALLINT* decimalDigitsPtr, SQLSMALLINT* nullablePtr ) ; FUNCTION: SQLRETURN SQLGetData ( SQLHSTMT statementHandle, SQLUSMALLINT columnNumber, SQLSMALLINT targetType, SQLPOINTER targetValuePtr, SQLINTEGER bufferLength, SQLINTEGER* strlen_or_indPtr ) ; : alloc-handle ( type parent -- handle ) f [ SQLAllocHandle ] keep swap succeeded? [ *void* ] [ drop f ] if ; : alloc-env-handle ( -- handle ) SQL-HANDLE-ENV SQL-NULL-HANDLE alloc-handle ; : alloc-dbc-handle ( env -- handle ) SQL-HANDLE-DBC swap alloc-handle ; : alloc-stmt-handle ( dbc -- handle ) SQL-HANDLE-STMT swap alloc-handle ; : temp-string ( length -- byte-array length ) [ CHAR: \space string>char-alien ] keep ; : odbc-init ( -- env ) alloc-env-handle [ SQL-ATTR-ODBC-VERSION SQL-OV-ODBC3 0 SQLSetEnvAttr succeeded? [ "odbc-init failed" throw ] unless ] keep ; : odbc-connect ( env dsn -- dbc ) >r alloc-dbc-handle dup r> f swap dup length 1024 temp-string 0 SQL-DRIVER-NOPROMPT SQLDriverConnect succeeded? [ "odbc-connect failed" throw ] unless ; : odbc-disconnect ( dbc -- ) SQLDisconnect succeeded? [ "odbc-disconnect failed" throw ] unless ; : odbc-prepare ( dbc string -- statement ) >r alloc-stmt-handle dup r> dup length SQLPrepare succeeded? [ "odbc-prepare failed" throw ] unless ; : odbc-free-statement ( statement -- ) SQL-HANDLE-STMT swap SQLFreeHandle succeeded? [ "odbc-free-statement failed" throw ] unless ; : odbc-execute ( statement -- ) SQLExecute succeeded? [ "odbc-execute failed" throw ] unless ; : odbc-next-row ( statement -- bool ) SQLFetch succeeded? ; : odbc-number-of-columns ( statement -- number ) 0 [ SQLNumResultCols succeeded? ] keep swap [ *short ] [ drop f ] if ; TUPLE: column nullable digits size type name number ; C: column : odbc-describe-column ( statement n -- column ) dup >r 1024 CHAR: \space string>char-alien dup >r 1024 0 0 dup >r 0 dup >r 0 dup >r 0 dup >r SQLDescribeCol succeeded? [ r> *short r> *short r> *uint r> *short convert-sql-type r> alien>char-string r> ] [ r> drop r> drop r> drop r> drop r> drop r> drop "odbc-describe-column failed" throw ] if ; : dereference-type-pointer ( byte-array column -- object ) column-type { { [ dup SQL-CHAR = ] [ drop alien>char-string ] } { [ dup SQL-VARCHAR = ] [ drop alien>char-string ] } { [ dup SQL-LONGVARCHAR = ] [ drop alien>char-string ] } { [ dup SQL-WCHAR = ] [ drop alien>char-string ] } { [ dup SQL-WCHARVAR = ] [ drop alien>char-string ] } { [ dup SQL-WLONGCHARVAR = ] [ drop alien>char-string ] } { [ dup SQL-SMALLINT = ] [ drop *short ] } { [ dup SQL-INTEGER = ] [ drop *long ] } { [ dup SQL-REAL = ] [ drop *float ] } { [ dup SQL-FLOAT = ] [ drop *double ] } { [ dup SQL-DOUBLE = ] [ drop *double ] } { [ dup SQL-TINYINT = ] [ drop *char ] } { [ dup SQL-BIGINT = ] [ drop *longlong ] } { [ t ] [ nip [ "Unknown SQL Type: " % word-name % ] "" make ] } } cond ; TUPLE: field value column ; C: field : odbc-get-field ( statement column -- field ) dup column? [ dupd odbc-describe-column ] unless dup >r column-number SQL-C-DEFAULT 8192 CHAR: \space string>char-alien dup >r 8192 f SQLGetData succeeded? [ r> r> [ dereference-type-pointer ] keep ] [ r> drop r> [ "SQLGetData Failed for Column: " % dup column-name % " of type: " % dup column-type word-name % ] "" make swap ] if ; : odbc-get-row-fields ( statement -- seq ) [ dup odbc-number-of-columns [ 1+ odbc-get-field field-value , ] curry* each ] { } make ; : (odbc-get-all-rows) ( statement -- ) dup odbc-next-row [ dup odbc-get-row-fields , yield (odbc-get-all-rows) ] [ drop ] if ; : odbc-get-all-rows ( statement -- seq ) [ (odbc-get-all-rows) ] { } make ; : odbc-query ( string dsn -- result ) odbc-init swap odbc-connect [ swap odbc-prepare dup odbc-execute dup odbc-get-all-rows swap odbc-free-statement ] keep odbc-disconnect ;