1 ! Copyright (C) 2007 Chris Double, 2016 Alexander Ilin.
2 ! See http://factorcode.org/license.txt for BSD license.
3 USING: accessors alien alien.c-types alien.data alien.libraries
4 alien.strings alien.syntax combinators continuations
5 io.encodings.ascii kernel locals make math sequences strings
7 FROM: alien.c-types => float short ;
10 << "odbc" "odbc32.dll" stdcall add-library >>
14 TYPEDEF: short SQLRETURN
15 TYPEDEF: short SQLSMALLINT
16 TYPEDEF: ushort SQLUSMALLINT
17 TYPEDEF: uint SQLUINTEGER
18 TYPEDEF: int SQLINTEGER
20 TYPEDEF: void* SQLHANDLE
21 TYPEDEF: void* SQLHENV
22 TYPEDEF: void* SQLHDBC
23 TYPEDEF: void* SQLHSTMT
24 TYPEDEF: void* SQLHWND
25 TYPEDEF: void* SQLPOINTER
27 CONSTANT: SQL-HANDLE-ENV 1
28 CONSTANT: SQL-HANDLE-DBC 2
29 CONSTANT: SQL-HANDLE-STMT 3
30 CONSTANT: SQL-HANDLE-DESC 4
32 CONSTANT: SQL-NULL-HANDLE f
34 CONSTANT: SQL-ATTR-ODBC-VERSION 200
36 : SQL-OV-ODBC2 ( -- number ) 2 <alien> ; inline
37 : SQL-OV-ODBC3 ( -- number ) 3 <alien> ; inline
39 CONSTANT: SQL-SUCCESS 0
40 CONSTANT: SQL-SUCCESS-WITH-INFO 1
41 CONSTANT: SQL-NO-DATA-FOUND 100
43 CONSTANT: SQL-DRIVER-NOPROMPT 0
44 CONSTANT: SQL-DRIVER-PROMPT 2
46 CONSTANT: SQL-C-DEFAULT 99
49 SQL-CHAR SQL-VARCHAR SQL-LONGVARCHAR
50 SQL-WCHAR SQL-WCHARVAR SQL-WLONGCHARVAR
51 SQL-DECIMAL SQL-SMALLINT SQL-NUMERIC SQL-INTEGER
52 SQL-REAL SQL-FLOAT SQL-DOUBLE
54 SQL-TINYINT SQL-BIGINT
55 SQL-BINARY SQL-VARBINARY SQL-LONGVARBINARY
56 SQL-TYPE-DATE SQL-TYPE-TIME SQL-TYPE-TIMESTAMP
57 SQL-TYPE-UTCDATETIME SQL-TYPE-UTCTIME
58 SQL-INTERVAL-MONTH SQL-INTERVAL-YEAR SQL-INTERVAL-DAY
59 SQL-INTERVAL-HOUR SQL-INTERVAL-MINUTE SQL-INTERVAL-SECOND
60 SQL-INTERVAL-YEAR-TO-MONTH
61 SQL-INTERVAL-DAY-TO-HOUR SQL-INTERVAL-DAY-TO-MINUTE
62 SQL-INTERVAL-DAY-TO-SECOND
63 SQL-INTERVAL-HOUR-TO-MINUTE SQL-INTERVAL-HOUR-TO-SECOND SQL-INTERVAL-MINUTE-TO-SECOND
67 : convert-sql-type ( number -- symbol )
70 { 12 [ SQL-VARCHAR ] }
71 { -1 [ SQL-LONGVARCHAR ] }
73 { -9 [ SQL-WCHARVAR ] }
74 { -10 [ SQL-WLONGCHARVAR ] }
76 { 5 [ SQL-SMALLINT ] }
83 { -6 [ SQL-TINYINT ] }
86 { -3 [ SQL-VARBINARY ] }
87 { -4 [ SQL-LONGVARBINARY ] }
88 { 91 [ SQL-TYPE-DATE ] }
89 { 92 [ SQL-TYPE-TIME ] }
90 { 93 [ SQL-TYPE-TIMESTAMP ] }
91 [ drop SQL-TYPE-UNKNOWN ]
94 : succeeded? ( n -- bool )
95 ! Did the call succeed (SQL-SUCCESS or SQL-SUCCESS-WITH-INFO)
98 { SQL-SUCCESS-WITH-INFO [ t ] }
102 FUNCTION: SQLRETURN SQLAllocHandle ( SQLSMALLINT handleType, SQLHANDLE inputHandle, SQLHANDLE* outputHandlePtr )
103 FUNCTION: SQLRETURN SQLSetEnvAttr ( SQLHENV environmentHandle, SQLINTEGER attribute, SQLPOINTER valuePtr, SQLINTEGER stringLength )
104 FUNCTION: SQLRETURN SQLDriverConnect ( SQLHDBC connectionHandle, SQLHWND windowHandle, SQLCHAR* inConnectionString, SQLSMALLINT stringLength, SQLCHAR* outConnectionString, SQLSMALLINT bufferLength, SQLSMALLINT* stringLength2Ptr, SQLUSMALLINT driverCompletion )
105 FUNCTION: SQLRETURN SQLDisconnect ( SQLHDBC connectionHandle )
106 FUNCTION: SQLRETURN SQLPrepare ( SQLHSTMT statementHandle, SQLCHAR* statementText, SQLINTEGER length )
107 FUNCTION: SQLRETURN SQLExecute ( SQLHSTMT statementHandle )
108 FUNCTION: SQLRETURN SQLFreeHandle ( SQLSMALLINT handleType, SQLHANDLE handle )
109 FUNCTION: SQLRETURN SQLFetch ( SQLHSTMT statementHandle )
110 FUNCTION: SQLRETURN SQLNumResultCols ( SQLHSTMT statementHandle, SQLSMALLINT* columnCountPtr )
111 FUNCTION: SQLRETURN SQLDescribeCol ( SQLHSTMT statementHandle, SQLSMALLINT columnNumber, SQLCHAR* columnName, SQLSMALLINT bufferLength, SQLSMALLINT* nameLengthPtr, SQLSMALLINT* dataTypePtr, SQLUINTEGER* columnSizePtr, SQLSMALLINT* decimalDigitsPtr, SQLSMALLINT* nullablePtr )
112 FUNCTION: SQLRETURN SQLGetData ( SQLHSTMT statementHandle, SQLUSMALLINT columnNumber, SQLSMALLINT targetType, SQLPOINTER targetValuePtr, SQLINTEGER bufferLength, SQLINTEGER* strlen_or_indPtr )
114 : alloc-handle ( type parent -- handle )
115 f void* <ref> [ SQLAllocHandle ] keep swap succeeded? [
121 : alloc-env-handle ( -- handle )
122 SQL-HANDLE-ENV SQL-NULL-HANDLE alloc-handle ;
124 : alloc-dbc-handle ( env -- handle )
125 SQL-HANDLE-DBC swap alloc-handle ;
127 : alloc-stmt-handle ( dbc -- handle )
128 SQL-HANDLE-STMT swap alloc-handle ;
132 : alien-space-str ( len -- alien )
133 CHAR: space <string> ascii string>alien ;
137 : temp-string ( length -- byte-array length )
138 [ alien-space-str ] keep ;
140 : odbc-init ( -- env )
143 SQL-ATTR-ODBC-VERSION SQL-OV-ODBC3 0 SQLSetEnvAttr
144 succeeded? [ "odbc-init failed" throw ] unless
147 : odbc-connect ( env dsn -- dbc )
148 [ alloc-dbc-handle dup ] dip
149 f swap ascii string>alien dup length
150 1024 temp-string 0 short <ref>
151 SQL-DRIVER-NOPROMPT SQLDriverConnect
152 succeeded? [ "odbc-connect failed" throw ] unless ;
154 : odbc-disconnect ( dbc -- )
155 SQLDisconnect succeeded? [ "odbc-disconnect failed" throw ] unless ;
157 : odbc-prepare ( dbc string -- statement )
158 [ alloc-stmt-handle dup ] dip ascii string>alien
159 dup length SQLPrepare
160 succeeded? [ "odbc-prepare failed" throw ] unless ;
162 : odbc-free-statement ( statement -- )
163 SQL-HANDLE-STMT swap SQLFreeHandle
164 succeeded? [ "odbc-free-statement failed" throw ] unless ;
166 : odbc-execute ( statement -- )
167 SQLExecute succeeded? [ "odbc-execute failed" throw ] unless ;
169 : odbc-next-row ( statement -- bool )
170 SQLFetch succeeded? ;
172 : odbc-number-of-columns ( statement -- number )
173 0 short <ref> [ SQLNumResultCols succeeded? ] keep swap [
179 TUPLE: column nullable digits size type name number ;
183 :: odbc-describe-column ( statement columnNumber -- column )
185 bufferLen alien-space-str :> columnName
186 0 short <ref> :> nameLengthPtr
187 0 short <ref> :> dataTypePtr
188 0 uint <ref> :> columnSizePtr
189 0 short <ref> :> decimalDigitsPtr
190 0 short <ref> :> nullablePtr
191 statement columnNumber columnName bufferLen nameLengthPtr
192 dataTypePtr columnSizePtr decimalDigitsPtr nullablePtr
193 SQLDescribeCol succeeded? [
194 nullablePtr short deref
195 decimalDigitsPtr short deref
196 columnSizePtr uint deref
197 dataTypePtr short deref convert-sql-type
198 columnName ascii alien>string
199 columnNumber <column>
201 "odbc-describe-column failed" throw
204 : dereference-type-pointer ( byte-array column -- object )
206 { SQL-CHAR [ ascii alien>string ] }
207 { SQL-VARCHAR [ ascii alien>string ] }
208 { SQL-LONGVARCHAR [ ascii alien>string ] }
209 { SQL-WCHAR [ ascii alien>string ] }
210 { SQL-WCHARVAR [ ascii alien>string ] }
211 { SQL-WLONGCHARVAR [ ascii alien>string ] }
212 { SQL-SMALLINT [ short deref ] }
213 { SQL-INTEGER [ long deref ] }
214 { SQL-REAL [ float deref ] }
215 { SQL-FLOAT [ double deref ] }
216 { SQL-DOUBLE [ double deref ] }
217 { SQL-TINYINT [ char deref ] }
218 { SQL-BIGINT [ longlong deref ] }
219 [ nip [ "Unknown SQL Type: " % name>> % ] "" make ]
222 TUPLE: field value column ;
226 :: odbc-get-field ( statement column! -- field )
228 statement column odbc-describe-column column!
231 bufferLen alien-space-str :> targetValuePtr
232 statement column number>> SQL-C-DEFAULT
233 targetValuePtr bufferLen f SQLGetData
235 targetValuePtr column [ dereference-type-pointer ] keep <field>
238 "SQLGetData Failed for Column: " %
240 " of type: " % dup type>> name>> %
241 ] "" make swap <field>
244 : odbc-get-row-fields ( statement -- seq )
246 dup odbc-number-of-columns iota [
247 1 + odbc-get-field value>> ,
251 : (odbc-get-all-rows) ( statement -- )
253 dup odbc-get-row-fields , yield (odbc-get-all-rows)
258 : odbc-get-all-rows ( statement -- seq )
259 [ (odbc-get-all-rows) ] { } make ;
261 : odbc-query ( string dsn -- result )
262 odbc-init swap odbc-connect [
266 dup odbc-get-all-rows
267 swap odbc-free-statement
269 ] [ odbc-disconnect ] [ ] cleanup ;