├── LICENSE ├── README.md ├── binds.odin ├── constants.odin ├── includes └── libmysqlclient.dylib ├── mod.pkg ├── mysql.odin └── time.odin /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2023 [Laytan](https://github.com/laytan) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Odin MySQL 2 | 3 | package mysql contains bindings for libmysqlclient, targeting MySQL 8.x. 4 | Almost all referenced functions in the manual found here: https://dev.mysql.com/doc/c-api/8.0/en/ 5 | have been added, excluding any deprecated functions and the client plugin system. 6 | -------------------------------------------------------------------------------- /binds.odin: -------------------------------------------------------------------------------- 1 | // file provides wrappers around the Bind datastructure that is used for prepared statements. 2 | // bindp_* are procedures that should be used with mysql.stmt_bind_param, and bindr_* with mysql.stmt_bind_result. 3 | // See for the C documentation: https://dev.mysql.com/doc/c-api/8.0/en/c-api-prepared-statement-data-structures.html 4 | package mysql 5 | 6 | import "core:c" 7 | import "core:time" 8 | 9 | @(private) 10 | Bind_Nil :: Bind { 11 | buffer_type = .Null, 12 | } 13 | 14 | @(private) 15 | not_nil := false 16 | @(private) 17 | is_nil := true 18 | 19 | bindp_nil :: proc() -> Bind { 20 | return Bind_Nil 21 | } 22 | 23 | // Binds the given string to the parameter, returns a pointer to the length, which you should free later. 24 | bindp_text :: proc(str: string, allocator := context.allocator) -> (bind: Bind, allocated_len: ^c.ulong) { 25 | str_len := new(c.ulong, allocator) 26 | str_len^ = c.ulong(len(str)) 27 | 28 | return Bind{ 29 | buffer_type = .String, 30 | buffer = raw_data(str), 31 | buffer_length = str_len^, 32 | length = str_len, 33 | is_null = ¬_nil, 34 | }, 35 | str_len 36 | } 37 | bindp_char :: bindp_text 38 | bindp_var_char :: bindp_text 39 | 40 | // Binds a buffer to write a result into, length_ptr will be set to the amount of bytes written into the buffer 41 | // after having executed the statement. 42 | // 43 | // If you want to allocate a buffer of the exact length of the stored value, you can do the following: 44 | // stmt: mysql.Statement 45 | // rbinds := make([^]mysql.Bind, 1) 46 | // code_len: c.ulong 47 | // rbinds[0] = mysql.bindr_text(nil, &code_len) 48 | // mysql.stmt_bind_result(stmt, raw_data(rbinds)) 49 | // mysql.stmt_store_result(stmt) 50 | // mysql.stmt_fetch(stmt) // After this call, code_len is set to the size of the stored value. 51 | // 52 | // code_buf := make([]byte, code_len) // Creates the buffer with the right length. 53 | // rbinds[0].buffer = raw_data(code_buf) 54 | // rbinds[0].buffer_length = code_len 55 | // mysql.stmt_fetch_column(stmt, raw_data(rbinds), 0, 0) // Will write the column into buffer. 56 | bindr_text :: proc(buf: []byte, length_ptr: ^c.ulong) -> Bind { 57 | return Bind{ 58 | buffer_type = .String, 59 | buffer = raw_data(buf), 60 | buffer_length = c.ulong(len(buf)), 61 | length = length_ptr, 62 | } 63 | } 64 | bindr_char :: bindr_text 65 | bindr_var_char :: bindr_text 66 | 67 | // Binds the given string to the parameter, returns a pointer to the length, which you should free later. 68 | bindp_blob :: proc(str: string, allocator := context.allocator) -> (bind: Bind, allocated_len: ^c.ulong) { 69 | str_len := new(c.ulong, allocator) 70 | str_len^ = c.ulong(len(str)) 71 | 72 | return Bind{ 73 | buffer_type = .Blob, 74 | buffer = raw_data(str), 75 | buffer_length = str_len^, 76 | length = str_len, 77 | is_null = ¬_nil, 78 | }, 79 | str_len 80 | } 81 | bindp_binary :: bindp_blob 82 | bindp_var_binary :: bindp_blob 83 | 84 | // Binds a buffer to write a result into, length_ptr will be set to the amount of bytes written into the buffer 85 | // after having executed the statement. 86 | // 87 | // If you want to allocate a buffer of the exact length of the stored value, you can do the following: 88 | // stmt: mysql.Statement 89 | // rbinds := make([^]mysql.Bind, 1) 90 | // code_len: c.ulong 91 | // rbinds[0] = mysql.bindr_text(nil, &code_len) 92 | // mysql.stmt_bind_result(stmt, raw_data(rbinds)) 93 | // mysql.stmt_store_result(stmt) 94 | // mysql.stmt_fetch(stmt) // After this call, code_len is set to the size of the stored value. 95 | // 96 | // code_buf := make([]byte, code_len) // Creates the buffer with the right length. 97 | // rbinds[0].buffer = raw_data(code_buf) 98 | // rbinds[0].buffer_length = code_len 99 | // mysql.stmt_fetch_column(stmt, raw_data(rbinds), 0, 0) // Will write the column into buffer. 100 | bindr_blob :: proc(buf: []byte, length_ptr: ^c.ulong) -> Bind { 101 | return Bind{ 102 | buffer_type = .String, 103 | buffer = raw_data(buf), 104 | buffer_length = c.ulong(len(buf)), 105 | length = length_ptr, 106 | } 107 | } 108 | bindr_binary :: bindr_blob 109 | bindr_var_binary :: bindr_blob 110 | 111 | bindp_tiny_int :: proc(ch: ^c.char) -> Bind { 112 | return Bind{buffer_type = .Tiny, buffer = ch, is_null = ch == nil ? &is_nil : ¬_nil} 113 | } 114 | 115 | bindr_tiny_int :: proc(i: ^c.char) -> Bind { 116 | return Bind{buffer_type = .Tiny, buffer = i} 117 | } 118 | 119 | bindp_small_int :: proc(i: ^c.short) -> Bind { 120 | return Bind{buffer_type = .Short, buffer = i, is_null = i == nil ? &is_nil : ¬_nil} 121 | } 122 | 123 | bindr_small_int :: proc(i: ^c.short) -> Bind { 124 | return Bind{buffer_type = .Short, buffer = i} 125 | } 126 | 127 | bindp_int :: proc(i: ^c.int) -> Bind { 128 | return Bind{buffer_type = .Long, buffer = i, is_null = i == nil ? &is_nil : ¬_nil} 129 | } 130 | 131 | bindr_int :: proc(i: ^c.int) -> Bind { 132 | return Bind{buffer_type = .Long, buffer = i} 133 | } 134 | 135 | bindp_big_int :: proc(i: ^c.longlong) -> Bind { 136 | return Bind{buffer_type = .Long_Long, buffer = i, is_null = i == nil ? &is_nil : ¬_nil} 137 | } 138 | 139 | bindr_big_int :: proc(i: ^c.longlong) -> Bind { 140 | return Bind{buffer_type = .Long_Long, buffer = i} 141 | } 142 | 143 | bindp_float :: proc(i: ^c.float) -> Bind { 144 | return Bind{buffer_type = .Float, buffer = i, is_null = i == nil ? &is_nil : ¬_nil} 145 | } 146 | 147 | bindr_float :: proc(i: ^c.float) -> Bind { 148 | return Bind{buffer_type = .Float, buffer = i} 149 | } 150 | 151 | bindp_double :: proc(i: ^c.double) -> Bind { 152 | return Bind{buffer_type = .Double, buffer = i, is_null = i == nil ? &is_nil : ¬_nil} 153 | } 154 | 155 | bindr_double :: proc(i: ^c.double) -> Bind { 156 | return Bind{buffer_type = .Double, buffer = i} 157 | } 158 | 159 | bindp_time_mysql :: proc(t: ^Time, type: Time_Type) -> Bind { 160 | return Bind{buffer_type = time_to_buffer_type(type), buffer = t} 161 | } 162 | 163 | bindp_time_time :: proc( 164 | t: time.Time, 165 | type: Time_Type, 166 | allocator := context.allocator, 167 | ) -> ( 168 | Bind, 169 | ^Time, 170 | ) { 171 | mt := new(Time, allocator) 172 | time_from_time(mt, t, type) 173 | return bindp_time_mysql(mt, type), mt 174 | } 175 | 176 | bindp_time :: proc { 177 | bindp_time_mysql, 178 | bindp_time_time, 179 | } 180 | 181 | bindr_time_mysql :: proc(t: ^Time, type: Time_Type) -> Bind { 182 | return Bind{buffer_type = time_to_buffer_type(type), buffer = t} 183 | } 184 | 185 | @(private) 186 | time_to_buffer_type :: proc (type: Time_Type) -> Buffer_Type { 187 | switch type { 188 | case .Time: return .Time 189 | case .Date_Time: return .Date_Time 190 | case .Timestamp: return .Timestamp 191 | case .Date: return .Date 192 | case: return nil 193 | } 194 | } 195 | -------------------------------------------------------------------------------- /includes/libmysqlclient.dylib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laytan/odin-mysql/ebe9609e9332d4dd9eb7fa7f9c4f498943183805/includes/libmysqlclient.dylib -------------------------------------------------------------------------------- /mod.pkg: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.1.2", 3 | "description": "MySQL database client bindings", 4 | "url": "https://github.com/laytan/odin-mysql", 5 | "readme": "README.md", 6 | "license": "MIT", 7 | "keywords": ["bindings", "mysql", "database"] 8 | } 9 | -------------------------------------------------------------------------------- /mysql.odin: -------------------------------------------------------------------------------- 1 | // package mysql contains bindings for libmysqlclient, targeting MySQL 8.x. 2 | // Almost all referenced functions in the manual found here: https://dev.mysql.com/doc/c-api/8.0/en/ 3 | // have been added, excluding any deprecated functions and the client plugin system. 4 | package mysql 5 | 6 | import "core:c" 7 | 8 | when ODIN_OS == .Darwin { 9 | foreign import lib "./includes/libmysqlclient.dylib" 10 | } else when ODIN_OS == .Windows { 11 | @(extra_linker_flags="/NODEFAULTLIB:libcmt") 12 | foreign import lib { 13 | "system:mysqlclient.lib", 14 | "system:Advapi32.lib", 15 | } 16 | } else { 17 | foreign import lib "system:mysqlclient" 18 | } 19 | 20 | // Opague structs, fields are never accessed (always through procedures), 21 | // so no need to define them in bindings. 22 | MySQL :: struct { 23 | __padding: [1160]byte, 24 | } 25 | 26 | Statement :: struct {} 27 | 28 | Result :: struct { 29 | row_count: c.uint64_t, 30 | fields: [^]Field, 31 | data: ^Data, 32 | data_cursor: ^Rows, 33 | lengths: [^]c.ulong, 34 | handle: ^MySQL, 35 | methods: rawptr, 36 | row: Row, 37 | current_row: Row, 38 | field_alloc: rawptr, 39 | field_count: c.uint, 40 | current_field: c.uint, 41 | eof: bool, 42 | unbuffered_fetch_cancelled: bool, 43 | metadata: Result_Metadata, 44 | extension: rawptr, 45 | } 46 | 47 | Data :: struct { 48 | data: ^Rows, 49 | alloc: rawptr, 50 | rows: c.uint64_t, 51 | fields: c.uint, 52 | } 53 | 54 | // https://dev.mysql.com/doc/c-api/8.0/en/c-api-prepared-statement-data-structures.html 55 | Bind :: struct { 56 | length: ^c.ulong, 57 | is_null: ^c.bool, 58 | buffer: rawptr, 59 | error: ^c.bool, 60 | _row_ptr: ^c.uchar, 61 | _store_param_func: rawptr, 62 | _fetch_result: rawptr, 63 | _skip_result: rawptr, 64 | buffer_length: c.ulong, 65 | _offset: c.ulong, 66 | _length_value: c.ulong, 67 | _param_number: c.int, 68 | _pack_length: c.int, 69 | buffer_type: Buffer_Type, 70 | _error_value: c.bool, 71 | is_unsigned: c.bool, 72 | _long_data_used: c.bool, 73 | _is_null_value: c.bool, 74 | _extension: rawptr, 75 | } 76 | 77 | // https://dev.mysql.com/doc/c-api/8.0/en/c-api-prepared-statement-type-codes.html 78 | // https://github.com/paulfitz/mysql-connector-c/blob/8c058fab669d61a14ec23c714e09c8dfd3ec08cd/include/mysql_com.h#L309 79 | Buffer_Type :: enum c.int { 80 | Decimal, 81 | Tiny, 82 | Short, 83 | Long, 84 | Float, 85 | Double, 86 | Null, 87 | Timestamp, 88 | Long_Long, 89 | Int24, 90 | Date, 91 | Time, 92 | Date_Time, 93 | Year, 94 | New_Date, 95 | Var_Char, 96 | Bit, 97 | New_Decimal = 246, 98 | Enum = 247, 99 | Set = 248, 100 | Tiny_Blob = 249, 101 | Medium_Blob = 250, 102 | Long_Blob = 251, 103 | Blob = 252, 104 | Var_String = 253, 105 | String = 254, 106 | Geometry = 255, 107 | } 108 | 109 | Option :: enum c.int { 110 | Opt_Connect_Timeout, 111 | Opt_Compress, 112 | Opt_Named_Pipe, 113 | Init_Command, 114 | Read_Default_File, 115 | Read_Default_Group, 116 | Set_Charset_Dir, 117 | Set_Charset_Name, 118 | Opt_Local_Infile, 119 | Opt_Protocol, 120 | Shared_Memory_Base_Name, 121 | Opt_Read_Timeout, 122 | Opt_Write_Timeout, 123 | Opt_Use_Result, 124 | Report_Data_Truncation, 125 | Opt_Reconnect, 126 | Plugin_Dir, 127 | Default_Auth, 128 | Opt_Bind, 129 | Opt_SSL_Key, 130 | Opt_SSL_Cert, 131 | Opt_SSL_CA, 132 | Opt_SSL_CA_Path, 133 | Opt_SSL_CA_Cipher, 134 | Opt_SSL_Crl, 135 | Opt_SSL_Crl_Path, 136 | Opt_Connect_Attr_Reset, 137 | Opt_Connect_Attr_Add, 138 | Opt_Connect_Attr_Delete, 139 | Server_Public_Key, 140 | Enable_Cleartext_Plugin, 141 | Opt_Can_Handle_Expired_Passwords, 142 | Opt_Max_Allowed_Packet, 143 | Opt_Net_Buffer_Length, 144 | Opt_TLS_Version, 145 | Opt_SSL_Mode, 146 | Opt_Get_Server_Public_Key, 147 | Opt_Retry_Count, 148 | Opt_Optional_Resultset_Metadata, 149 | Opt_SSL_Fips_Mode, 150 | Opt_TLS_Cipher_Suites, 151 | Opt_Compression_Algorithms, 152 | Opt_Zstd_Compression_Level, 153 | Opt_Load_Data_Local_Dir, 154 | Opt_User_Password, 155 | Opt_SSL_Session_Data, 156 | } 157 | 158 | Result_Metadata :: enum c.int { 159 | None = 0, 160 | Full = 1, 161 | } 162 | 163 | Session_State_Type :: enum c.int { 164 | Track_System_Variables, 165 | Track_Schema, 166 | Track_State_Change, 167 | Track_Gtids, 168 | Track_Transaction_Characteristics, 169 | Track_Transaction_State, 170 | } 171 | 172 | Server_Option :: enum c.int { 173 | Multi_Statements_On, 174 | Multi_Statements_Off, 175 | } 176 | 177 | Statement_Attr_Type :: enum c.int { 178 | Update_Max_Length, 179 | Cursor_Type, 180 | Prefetch_Rows, 181 | } 182 | 183 | Net_Async_Status :: enum c.int { 184 | Complete = 0, 185 | Not_Ready, 186 | Error, 187 | Complete_No_More_Results, 188 | } 189 | 190 | Field_Type :: enum c.int { 191 | Decimal, 192 | Tiny, 193 | Short, 194 | Long, 195 | Float, 196 | Double, 197 | Null, 198 | Timestamp, 199 | Long_Long, 200 | Int24, 201 | Date, 202 | Time, 203 | Date_Time, 204 | Year, 205 | New_Date, 206 | Var_Char, 207 | Bit, 208 | Timestamp2, 209 | Date_Time2, 210 | Time2, 211 | Typed_Array, 212 | Invalid = 243, 213 | Bool = 244, 214 | Json = 245, 215 | New_Decimal = 246, 216 | Enum = 247, 217 | Set = 248, 218 | Tiny_Blob = 249, 219 | Medium_Blob = 250, 220 | Long_Blob = 251, 221 | Blob = 252, 222 | Var_String = 253, 223 | String = 254, 224 | Geometry = 255, 225 | } 226 | 227 | Field :: struct { 228 | name: cstring, 229 | org_name: cstring, 230 | table: cstring, 231 | org_table: cstring, 232 | db: cstring, 233 | catalog: cstring, 234 | def: cstring, 235 | length: c.ulong, 236 | max_length: c.ulong, 237 | name_length: c.uint, 238 | org_name_length: c.uint, 239 | table_length: c.uint, 240 | org_table_length: c.uint, 241 | db_length: c.uint, 242 | catalog_length: c.uint, 243 | def_length: c.uint, 244 | flags: c.uint, 245 | decimals: c.uint, 246 | charsetnr: c.uint, 247 | type: Field_Type, 248 | extension: rawptr, 249 | } 250 | 251 | Field_Offset :: c.uint 252 | 253 | Row :: [^]cstring 254 | 255 | Rows :: struct { 256 | next: ^Rows, 257 | data: Row, 258 | length: c.ulong, 259 | } 260 | 261 | Row_Offset :: ^Rows 262 | 263 | Charset_Info :: struct { 264 | number: c.uint, 265 | state: c.uint, 266 | csname: cstring, 267 | name: cstring, 268 | comment: cstring, 269 | dir: cstring, 270 | mb_min_len: c.uint, 271 | mb_max_len: c.uint, 272 | } 273 | 274 | Rpl :: struct { 275 | file_name_length: c.size_t, 276 | file_name: cstring, 277 | start_position: c.uint64_t, 278 | server_id: c.uint, 279 | flags: c.uint, 280 | gtid_set_encoded_size: c.size_t, 281 | fix_gtid_set: proc(rpl: ^Rpl, packet_gtid_set: cstring), 282 | gtid_set_arg: rawptr, 283 | size: c.ulong, 284 | buffer: cstring, 285 | } 286 | 287 | @(link_prefix = "mysql_") 288 | foreign lib { 289 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-affected-rows.html 290 | affected_rows :: proc(mysql: ^MySQL) -> c.uint64_t --- 291 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-autocommit.html 292 | autocommit :: proc(mysql: ^MySQL, mode: c.bool) -> c.bool --- 293 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-bind-param.html 294 | bind_param :: proc(mysql: ^MySQL, n_params: c.uint, bind: [^]Bind, name: [^]cstring) -> c.bool --- 295 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-change-user.html 296 | change_user :: proc(mysql: ^MySQL, user: cstring, password: cstring, db: cstring) -> c.bool --- 297 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-character-set-name.html 298 | character_set_name :: proc(mysql: ^MySQL) -> cstring --- 299 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-close.html 300 | close :: proc(mysql: ^MySQL) --- 301 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-commit.html 302 | commit :: proc(mysql: ^MySQL) -> c.bool --- 303 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-real-connect.html 304 | @(link_name = "mysql_real_connect") 305 | connect :: proc(mysql: ^MySQL, host: cstring, user: cstring, passwd: cstring, db: cstring, port: c.uint = 0, unix_socket: cstring = nil, client_flag: c.ulong = 0) -> ^MySQL --- 306 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-real-connect-dns-srv.html 307 | @(link_name = "mysql_real_connect_dns_srv") 308 | connect_dns_srv :: proc(mysql: ^MySQL, dns_srv_name: cstring, user: cstring, passwd: cstring, db: cstring, client_flag: c.ulong = 0) -> ^MySQL --- 309 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-data-seek.html 310 | data_seek :: proc(result: ^Result, offset: c.uint64_t) --- 311 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-dump-debug-info.html 312 | dump_debug_info :: proc(mysql: ^MySQL) -> c.int --- 313 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-errno.html 314 | errno :: proc(mysql: ^MySQL) -> Errno --- 315 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-error.html 316 | error :: proc(mysql: ^MySQL) -> cstring --- 317 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-real-escape-string.html 318 | @(link_name = "mysql_real_escape_string") 319 | escape_string :: proc(mysql: ^MySQL, to: [^]byte, from: cstring, length: c.ulong) -> c.ulong --- 320 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-real-escape-string-quote.html 321 | @(link_name = "mysql_real_escape_string_quote") 322 | escape_string_quote :: proc(mysql: ^MySQL, to: [^]byte, from: cstring, length: c.ulong, quote: c.char) -> c.ulong --- 323 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-fetch-field.html 324 | fetch_field :: proc(result: ^Result) -> ^Field --- 325 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-fetch-field-direct.html 326 | fetch_field_direct :: proc(result: ^Result, fieldnr: c.uint) -> ^Field --- 327 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-fetch-fields.html 328 | fetch_fields :: proc(result: ^Result) -> [^]Field --- 329 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-fetch-lengths.html 330 | fetch_lengths :: proc(result: ^Result) -> [^]c.ulong --- 331 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-fetch-row.html 332 | fetch_row :: proc(result: ^Result) -> Row --- 333 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-field-count.html 334 | field_count :: proc(mysql: ^MySQL) -> c.uint --- 335 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-field-seek.html 336 | field_seek :: proc(result: ^Result, offset: Field_Offset) -> Field_Offset --- 337 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-field-tell.html 338 | field_tell :: proc(result: ^Result) -> Field_Offset --- 339 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-free-result.html 340 | free_result :: proc(result: ^Result) --- 341 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-free-ssl-session-data.html 342 | free_ssl_session_data :: proc(mysql: ^MySQL, data: rawptr) -> c.bool --- 343 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-get-character-set-info.html 344 | get_character_set_info :: proc(mysql: ^MySQL, info: ^Charset_Info) --- 345 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-get-client-info.html 346 | get_client_info :: proc() -> cstring --- 347 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-get-client-version.html 348 | get_client_version :: proc() -> c.ulong --- 349 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-get-host-info.html 350 | get_host_info :: proc(mysql: ^MySQL) -> cstring --- 351 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-get-option.html 352 | get_option :: proc(mysql: ^MySQL, option: Option, arg: rawptr) -> c.int --- 353 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-get-proto-info.html 354 | get_proto_info :: proc(mysql: ^MySQL) -> c.uint --- 355 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-get-server-info.html 356 | get_server_info :: proc(mysql: ^MySQL) -> cstring --- 357 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-get-server-version.html 358 | get_server_version :: proc(mysql: ^MySQL) -> c.ulong --- 359 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-get-ssl-cipher.html 360 | get_ssl_cypher :: proc(mysql: ^MySQL) -> cstring --- 361 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-get-ssl-session-data.html 362 | get_ssl_session_data :: proc(mysql: ^MySQL, n_ticket: c.uint, out_len: ^c.uint) -> rawptr --- 363 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-get-ssl-session-reused.html 364 | get_ssl_session_reused :: proc(mysql: ^MySQL) -> c.bool --- 365 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-info.html 366 | info :: proc(mysql: ^MySQL) -> cstring --- 367 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-init.html 368 | init :: proc(mysql: ^MySQL = nil) -> ^MySQL --- 369 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-insert-id.html 370 | insert_id :: proc(mysql: ^MySQL) -> c.uint64_t --- 371 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-library-end.html 372 | @(link_name = "mysql_server_end") 373 | library_end :: proc() --- 374 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-library-init.html 375 | @(link_name = "mysql_server_init") 376 | library_init :: proc(argc: c.int, argv: [^]cstring, groups: [^]cstring) -> c.int --- 377 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-list-dbs.html 378 | list_dbs :: proc(mysql: ^MySQL, wild: cstring) -> ^Result --- 379 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-list-tables.html 380 | list_tables :: proc(mysql: ^MySQL, wild: cstring) -> ^Result --- 381 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-more-results.html 382 | more_results :: proc(mysql: ^MySQL) -> c.bool --- 383 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-next-result.html 384 | next_result :: proc(mysql: ^MySQL) -> c.int --- 385 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-num-fields.html 386 | num_fields :: proc(result: ^Result) -> c.uint --- 387 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-num-rows.html 388 | num_rows :: proc(result: ^Result) -> c.uint64_t --- 389 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-options.html 390 | options :: proc(mysql: ^MySQL, option: Option, arg: rawptr) -> c.int --- 391 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-options4.html 392 | options4 :: proc(mysql: ^MySQL, option: Option, arg1: rawptr, arg2: rawptr) -> c.int --- 393 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-ping.html 394 | ping :: proc(mysql: ^MySQL) -> c.int --- 395 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-query.html 396 | @(link_name = "mysql_real_query") 397 | query :: proc(mysql: ^MySQL, stmt_str: cstring, length: c.ulong) -> c.int --- 398 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-reload.html 399 | reload :: proc(mysql: ^MySQL) -> c.int --- 400 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-reset-connection.html 401 | reset_connection :: proc(mysql: ^MySQL) -> c.int --- 402 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-reset-server-public-key.html 403 | reset_server_public_key :: proc() --- 404 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-result-metadata.html 405 | result_metadata :: proc(result: ^Result) -> Result_Metadata --- 406 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-rollback.html 407 | rollback :: proc(mysql: ^MySQL) -> c.bool --- 408 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-row-seek.html 409 | row_seek :: proc(result: ^Result, offset: Row_Offset) -> Row_Offset --- 410 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-row-tell.html 411 | row_tell :: proc(result: ^Result) -> Row_Offset --- 412 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-select-db.html 413 | select_db :: proc(mysql: ^MySQL, db: cstring) -> c.int --- 414 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-session-track-get-first.html 415 | session_track_get_first :: proc(mysql: ^MySQL, type: Session_State_Type, data: ^cstring, length: ^c.size_t) -> c.int --- 416 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-session-track-get-next.html 417 | session_track_get_next :: proc(mysql: ^MySQL, type: Session_State_Type, data: ^cstring, length: ^c.size_t) -> c.int --- 418 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-set-character-set.html 419 | set_character_set :: proc(mysql: ^MySQL, cs_name: cstring) -> c.int --- 420 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-set-local-infile-default.html 421 | set_local_infile_default :: proc(mysql: ^MySQL) --- 422 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-set-local-infile-handler.html 423 | set_local_infile_handler :: proc(mysql: ^MySQL, local_infile_init: proc "cdecl" (ptr: ^rawptr, filename: cstring, user_data: rawptr) -> c.int, local_infile_read: proc "cdecl" (ptr: rawptr, buf: cstring, buf_len: c.uint) -> c.int, local_infile_end: proc "cdecl" (ptr: rawptr), local_infile_error: proc "cdecl" (ptr: rawptr, error_msg: cstring, error_msg_len: c.uint) -> c.int, user_data: rawptr) --- 424 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-set-server-option.html 425 | set_server_option :: proc(mysql: ^MySQL, option: Server_Option) -> c.int --- 426 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-sqlstate.html 427 | sqlstate :: proc(mysql: ^MySQL) -> cstring --- 428 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-ssl-set.html 429 | ssl_set :: proc(mysql: ^MySQL, key: cstring, cert: cstring, ca: cstring, capath: cstring, cipher: cstring) -> c.bool --- 430 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-stat.html 431 | stat :: proc(mysql: ^MySQL) -> cstring --- 432 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-store-result.html 433 | store_result :: proc(mysql: ^MySQL) -> ^Result --- 434 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-thread-id.html 435 | thread_id :: proc(mysql: ^MySQL) -> c.ulong --- 436 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-use-result.html 437 | use_result :: proc(mysql: ^MySQL) -> ^Result --- 438 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-warning-count.html 439 | warning_count :: proc(mysql: ^MySQL) -> c.uint --- 440 | 441 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-stmt-affected-rows.html 442 | stmt_affected_rows :: proc(stmt: ^Statement) -> c.uint64_t --- 443 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-stmt-attr-get.html 444 | stmt_attr_get :: proc(stmt: ^Statement, option: Statement_Attr_Type, arg: rawptr) -> c.bool --- 445 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-stmt-attr-set.html 446 | stmt_attr_set :: proc(stmt: ^Statement, option: Statement_Attr_Type, arg: rawptr) -> c.bool --- 447 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-stmt-bind-param.html 448 | stmt_bind_param :: proc(stmt: ^Statement, bind: [^]Bind) -> c.bool --- 449 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-stmt-bind-result.html 450 | stmt_bind_result :: proc(stmt: ^Statement, bind: [^]Bind) -> c.bool --- 451 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-stmt-close.html 452 | stmt_close :: proc(stmt: ^Statement) -> c.bool --- 453 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-stmt-data-seek.html 454 | stmt_data_seek :: proc(stmt: ^Statement, offset: c.uint64_t) --- 455 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-stmt-errno.html 456 | stmt_errno :: proc(stmt: ^Statement) -> Errno --- 457 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-stmt-error.html 458 | stmt_error :: proc(stmt: ^Statement) -> cstring --- 459 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-stmt-execute.html 460 | stmt_execute :: proc(stmt: ^Statement) -> c.int --- 461 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-stmt-fetch.html 462 | stmt_fetch :: proc(stmt: ^Statement) -> c.int --- 463 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-stmt-fetch-column.html 464 | stmt_fetch_column :: proc(stmt: ^Statement, bind: ^Bind, column: c.uint, offset: c.ulong) -> c.int --- 465 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-stmt-field-count.html 466 | stmt_field_count :: proc(stmt: ^Statement) -> c.uint --- 467 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-stmt-free-result.html 468 | stmt_free_result :: proc(stmt: ^Statement) -> c.bool --- 469 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-stmt-init.html 470 | stmt_init :: proc(mysql: ^MySQL) -> ^Statement --- 471 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-stmt-insert-id.html 472 | stmt_insert_id :: proc(stmt: ^Statement) -> c.uint64_t --- 473 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-stmt-next-result.html 474 | stmt_next_result :: proc(stmt: ^Statement) -> c.int --- 475 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-stmt-num-rows.html 476 | stmt_num_rows :: proc(stmt: ^Statement) -> c.uint64_t --- 477 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-stmt-param-count.html 478 | stmt_param_count :: proc(stmt: ^Statement) -> c.ulong --- 479 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-stmt-param-metadata.html 480 | stmt_param_metadata :: proc(stmt: ^Statement) -> ^Result --- 481 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-stmt-prepare.html 482 | stmt_prepare :: proc(stmt: ^Statement, stmt_str: cstring, stmt_str_len: c.ulong) -> c.int --- 483 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-stmt-reset.html 484 | stmt_reset :: proc(stmt: ^Statement) -> c.bool --- 485 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-stmt-result-metadata.html 486 | stmt_result_metadata :: proc(stmt: ^Statement) -> ^Result --- 487 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-stmt-row-seek.html 488 | stmt_row_seek :: proc(stmt: ^Statement, offset: Row_Offset) -> Row_Offset --- 489 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-stmt-row-tell.html 490 | stmt_row_tell :: proc(stmt: ^Statement) -> Row_Offset --- 491 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-stmt-send-long-data.html 492 | stmt_send_long_data :: proc(stmt: ^Statement, parameter_number: c.uint, data: cstring, length: c.ulong) -> c.bool --- 493 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-stmt-sqlstate.html 494 | stmt_sqlstate :: proc(stmt: ^Statement) -> cstring --- 495 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-stmt-store-result.html 496 | stmt_store_result :: proc(stmt: ^Statement) -> c.int --- 497 | 498 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-fetch-row-nonblocking.html 499 | fetch_row_nonblocking :: proc(result: ^Result, row: ^Row) -> Net_Async_Status --- 500 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-free-result-nonblocking.html 501 | free_result_nonblocking :: proc(result: ^Result) -> Net_Async_Status --- 502 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-next-result-nonblocking.html 503 | next_result_nonblocking :: proc(mysql: ^MySQL) -> Net_Async_Status --- 504 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-real-connect-nonblocking.html 505 | @(link_name = "mysql_real_connect_nonblocking") 506 | connect_nonblocking :: proc(mysql: ^MySQL, host: cstring, user: cstring, passwd: cstring, db: cstring, port: c.uint = 0, unix_socket: cstring = nil, client_flag: c.ulong = 0) -> Net_Async_Status --- 507 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-real-query-nonblocking.html 508 | @(link_name = "mysql_real_query_nonblocking") 509 | query_nonblocking :: proc(mysql: ^MySQL, stmt: cstring, length: c.ulong) -> Net_Async_Status --- 510 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-store-result-nonblocking.html 511 | store_result_nonblocking :: proc(mysql: ^MySQL, result: ^^Result) -> Net_Async_Status --- 512 | 513 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-thread-end.html 514 | thread_end :: proc() --- 515 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-thread-init.html 516 | thread_init :: proc() -> c.bool --- 517 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-thread-safe.html 518 | thread_safe :: proc() -> c.uint --- 519 | 520 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-binlog-close.html 521 | binlog_close :: proc(mysql: ^MySQL, rpl: ^Rpl) --- 522 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-binlog-fetch.html 523 | binlog_fetch :: proc(mysql: ^MySQL, rpl: ^Rpl) -> c.int --- 524 | // https://dev.mysql.com/doc/c-api/8.0/en/mysql-binlog-open.html 525 | binlog_open :: proc(mysql: ^MySQL, rpl: ^Rpl) -> c.int --- 526 | } 527 | 528 | -------------------------------------------------------------------------------- /time.odin: -------------------------------------------------------------------------------- 1 | package mysql 2 | 3 | import "core:c" 4 | import "core:time" 5 | 6 | Timestamp_Type :: enum c.int { 7 | None = -2, 8 | Error = -1, 9 | // Stores year, month and day. 10 | Date = 0, 11 | // Stores all components, UTC for TIMESTAMP and local for DATETIME. 12 | Date_Time = 1, 13 | // Stores hours, minute, second and microsecond. 14 | Time = 2, 15 | // Temporary type for DATETIME or TIMESTAMP with timezone info. Converted to Date_Time after timezone info is reconciled. 16 | Date_Time_Tz = 3, 17 | } 18 | 19 | // The actual database column types. 20 | Time_Type :: enum { 21 | Time, 22 | Date, 23 | Date_Time, 24 | Timestamp, 25 | } 26 | 27 | Time :: struct { 28 | year: c.uint, 29 | month: c.uint, 30 | day: c.uint, 31 | hour: c.uint, 32 | minute: c.uint, 33 | second: c.uint, 34 | microsecond: c.ulong, 35 | neg: c.bool, 36 | time_type: Timestamp_Type, 37 | time_zone_displacement: c.int, // In seconds. 38 | } 39 | 40 | @(private) 41 | MICROSECONDS_PER_SECOND :: 1e+6 42 | 43 | @(private) 44 | time_parts :: proc(t: time.Time) -> (c.uint, c.uint, c.uint, c.ulong) { 45 | t := time.now() 46 | nsec := time.to_unix_nanoseconds(t) 47 | micro := c.ulong(((nsec / 1000) % MICROSECONDS_PER_SECOND)) 48 | hour, min, sec := time.clock_from_time(t) 49 | return c.uint(hour), c.uint(min), c.uint(sec), micro 50 | } 51 | 52 | @(private) 53 | date_parts :: proc(t: time.Time) -> (year, month, day: c.uint) { 54 | ayear, amonth, aday := time.date(t) 55 | return c.uint(ayear), c.uint(amonth), c.uint(aday) 56 | } 57 | 58 | time_from_time :: proc(result: ^Time, t: time.Time, type: Time_Type) { 59 | switch type { 60 | case .Date: 61 | result.time_type = .Date 62 | result.year, result.month, result.day = date_parts(t) 63 | return 64 | case .Time: 65 | result.time_type = .Time 66 | result.hour, result.minute, result.second, result.microsecond = time_parts(t) 67 | return 68 | // TODO: odin doesn't really support timezones, I think it is always UTC. 69 | case .Date_Time, .Timestamp: 70 | result.time_type = .Date_Time 71 | result.year, result.month, result.day = date_parts(t) 72 | result.hour, result.minute, result.second, result.microsecond = time_parts(t) 73 | return 74 | case: 75 | panic("unreachable") 76 | } 77 | } 78 | --------------------------------------------------------------------------------