diff --git a/Local.session.sql b/Local.session.sql new file mode 100644 index 0000000..6bdc00f --- /dev/null +++ b/Local.session.sql @@ -0,0 +1,7 @@ +SELECT * FROM pg_class WHERE oid = 16388; + +SELECT * FROM pg_type; + + +ALTER TABLE editors +ADD COLUMN release DATE; \ No newline at end of file diff --git a/src/drivers/postgres_client.sml b/src/drivers/postgres_client.sml index bf7c707..25dc96b 100644 --- a/src/drivers/postgres_client.sml +++ b/src/drivers/postgres_client.sml @@ -12,6 +12,18 @@ struct infix 3 ||> fun (x, y) ||> f = f (x, y) (* Left application *) + datatype SQLType + = Varchar + | Date4 + + val postgresTypeTable = + (* TODO: Refactor later with a proper hashmap *) + let val table = [] + in ( + (1082, Date4)::(1043, Varchar)::table + ) + end + fun convertString (elem: string) = (Word8Vector.fromList (List.map Byte.charToByte (String.explode elem))) @@ -147,9 +159,33 @@ struct end handle ex => Connection.Failure (exnMessage ex) - type QueryResult = {name: string, sqlType: string, records: string list} + datatype TypeSize = + Variable + | Fixed of int + type QueryResult = + { name: string, + sqlTypeId: int, + records: string list, + tableObjectId: int, + column: int, + sqlTypeSize: TypeSize } val results: QueryResult list ref = ref [] + fun display () = + let + val str = PolyML.makestring (!results) + in print (str ^ "\n") + end + + fun debug something = + let + val str = PolyML.makestring something + in print (str ^ "\n") + end + + fun getType identifier = + HashTable.find postgresTypeTable identifier + fun fetch (conn: Connection.connection) = let val messageType = Socket.recvVec (conn, 1) @@ -157,24 +193,38 @@ struct fun convertHeaders contents _ = let val reference = !contents + val foundIndex = + Word8VectorSlice.findi (fn (_, byte) => byte = 0wx0) reference + |> Option.map (fn (index, _) => index) + fun composeType initialIndex = + let + val composition: QueryResult = + { name = Byte.bytesToString (Word8VectorSlice.concat [Word8VectorSlice.subslice (reference, 0, SOME initialIndex)]) + , tableObjectId = bytesToInt32 (Word8VectorSlice.concat [Word8VectorSlice.subslice (reference, initialIndex + 1, SOME 4)]) + , records = [] + , column = bytesToInt16 (Word8VectorSlice.concat [Word8VectorSlice.subslice (reference, initialIndex + 5, SOME 2)]) + , sqlTypeId = bytesToInt32 (Word8VectorSlice.concat [Word8VectorSlice.subslice (reference, initialIndex + 7, SOME 4)]) + , sqlTypeSize = + let val sqlTypeSize = bytesToInt16 (Word8VectorSlice.concat [Word8VectorSlice.subslice (reference, initialIndex + 11, SOME 2)]) + in if sqlTypeSize < 0 + then Variable + else Fixed sqlTypeSize + end } + in composition + end in - case Word8VectorSlice.findi (fn (_, byte) => byte = 0wx0) (!contents) of - SOME (index, _) => - ( contents - := - Word8VectorSlice.subslice + case foundIndex of + SOME stringIndex => ( + contents + := Word8VectorSlice.subslice ( !contents - , index + , stringIndex + 19 (* integers in the message for this field + 1 NULL byte *) , NONE - ) - ; { name = Byte.bytesToString (Word8VectorSlice.concat - [Word8VectorSlice.subslice (reference, 0, SOME index)]) - , sqlType = "X" - , records = [] - } - ) + ); + composeType stringIndex + ) | NONE => raise Fail "Could not find null terminator" end fun convertData (elem: QueryResult) contents : QueryResult = @@ -187,19 +237,23 @@ struct ~1 => ( contents := Word8VectorSlice.subslice (!contents, 4, NONE) ; { name = (#name elem) - , sqlType = (#sqlType elem) + , sqlTypeId = (#sqlTypeId elem) , records = "NULL" :: (#records elem) - } + , column = (#column elem) + , tableObjectId = (#tableObjectId elem) + , sqlTypeSize = (#sqlTypeSize elem) } ) | n => ( contents := Word8VectorSlice.subslice (!contents, 4 + n, NONE) ; { name = (#name elem) - , sqlType = (#sqlType elem) + , sqlTypeId = (#sqlTypeId elem) , records = (Byte.bytesToString (Word8VectorSlice.concat [Word8VectorSlice.subslice (reference, 4, SOME n)])) :: (#records elem) - } + , column = (#column elem) + , tableObjectId = (#tableObjectId elem) + , sqlTypeSize = (#sqlTypeSize elem) } ) end in @@ -284,10 +338,4 @@ struct execute(conn, queryString); !results ) - - fun display () = - let - val str = PolyML.makestring (!results) - in print (str ^ "\n") - end end diff --git a/src/vendor_driver.sig b/src/vendor_driver.sig index 47146b4..b5c7abf 100644 --- a/src/vendor_driver.sig +++ b/src/vendor_driver.sig @@ -1,6 +1,15 @@ signature VENDOR_DRIVER = sig - type QueryResult = {name: string, sqlType: string, records: string list} + datatype TypeSize = + Variable + | Fixed of int + type QueryResult = + { name: string, + sqlTypeId: int, + records: string list, + tableObjectId: int, + column: int, + sqlTypeSize: TypeSize } val connect: string * string * string * int -> Connection.t val execute: Connection.connection * string -> unit val query: Connection.connection * string -> QueryResult list diff --git a/tests/main.sml b/tests/main.sml index 8b56d40..ff41399 100644 --- a/tests/main.sml +++ b/tests/main.sml @@ -4,13 +4,15 @@ fun main () = in case conn of Connection.Success conn => - ( PostgresClient.execute + ( + (* PostgresClient.execute ( conn , "CREATE TABLE editors(name VARCHAR(20) NOT NULL PRIMARY KEY, language VARCHAR(20) NOT NULL);" ) ; PostgresClient.execute (conn, "INSERT INTO editors(name, language) VALUES ('GNU/Emacs', 'Emacs Lisp'), ('Vim', 'Vimscript');") - ; PostgresClient.execute + ; *) + PostgresClient.execute (conn, "SELECT * FROM editors;") ; PostgresClient.display () ; PostgresClient.close conn