├── .editorconfig
├── .gitignore
├── LICENSE
├── bindings
├── unix.jai
└── windows.jai
├── byte_order.jai
├── examples
├── example.jai
└── first.jai
├── first.jai
├── generate.jai
├── generate_types.jai
├── module.jai
├── pq_types.jai
└── readme.md
/.editorconfig:
--------------------------------------------------------------------------------
1 | # EditorConfig helps developers define and maintain consistent
2 | # coding styles between different editors and IDEs
3 | # editorconfig.org
4 |
5 | root = true
6 |
7 | [*]
8 | indent_style = space
9 | indent_size = 4
10 |
11 | # We recommend you to keep these unchanged
12 | end_of_line = lf
13 | charset = utf-8
14 | trim_trailing_whitespace = true
15 | insert_final_newline = true
16 |
17 | [*.md]
18 | trim_trailing_whitespace = false
19 |
20 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .build/
2 | pgtest
3 | examples/example
4 | *.dSYM
5 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Raphael Luba
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 |
--------------------------------------------------------------------------------
/bindings/unix.jai:
--------------------------------------------------------------------------------
1 | //
2 | // This file was auto-generated using the following command:
3 | //
4 | // jai generate.jai
5 | //
6 |
7 |
8 |
9 | InvalidOid :: cast(Oid) 0;
10 |
11 | PG_COPYRES_ATTRS :: 0x01;
12 | PG_COPYRES_TUPLES :: 0x02;
13 | PG_COPYRES_EVENTS :: 0x04;
14 | PG_COPYRES_NOTICEHOOKS :: 0x08;
15 |
16 | PQnoPasswordSupplied :: "fe_sendauth: no password supplied\n";
17 |
18 | /*
19 | * Object ID is a fundamental type in Postgres.
20 | */
21 | Oid :: u32;
22 |
23 | /* Define a signed 64-bit integer type for use in client API declarations. */
24 | pg_int64 :: s64;
25 |
26 | /*
27 | * Although it is okay to add to these lists, values which become unused
28 | * should never be removed, nor should constants be redefined - that would
29 | * break compatibility with existing code.
30 | */
31 | ConnStatusType :: enum u32 {
32 | CONNECTION_OK :: 0;
33 | CONNECTION_BAD :: 1;
34 |
35 | CONNECTION_STARTED :: 2;
36 | CONNECTION_MADE :: 3;
37 | CONNECTION_AWAITING_RESPONSE :: 4;
38 |
39 | CONNECTION_AUTH_OK :: 5;
40 |
41 | CONNECTION_SETENV :: 6;
42 | CONNECTION_SSL_STARTUP :: 7;
43 | CONNECTION_NEEDED :: 8;
44 | CONNECTION_CHECK_WRITABLE :: 9;
45 |
46 | CONNECTION_CONSUME :: 10;
47 | }
48 |
49 | PostgresPollingStatusType :: enum u32 {
50 | PGRES_POLLING_FAILED :: 0;
51 | PGRES_POLLING_READING :: 1;
52 | PGRES_POLLING_WRITING :: 2;
53 | PGRES_POLLING_OK :: 3;
54 | PGRES_POLLING_ACTIVE :: 4;
55 | }
56 |
57 | ExecStatusType :: enum u32 {
58 | PGRES_EMPTY_QUERY :: 0;
59 | PGRES_COMMAND_OK :: 1;
60 |
61 | PGRES_TUPLES_OK :: 2;
62 |
63 | PGRES_COPY_OUT :: 3;
64 | PGRES_COPY_IN :: 4;
65 | PGRES_BAD_RESPONSE :: 5;
66 |
67 | PGRES_NONFATAL_ERROR :: 6;
68 | PGRES_FATAL_ERROR :: 7;
69 | PGRES_COPY_BOTH :: 8;
70 | PGRES_SINGLE_TUPLE :: 9;
71 | }
72 |
73 | PGTransactionStatusType :: enum u32 {
74 | PQTRANS_IDLE :: 0;
75 | PQTRANS_ACTIVE :: 1;
76 | PQTRANS_INTRANS :: 2;
77 | PQTRANS_INERROR :: 3;
78 | PQTRANS_UNKNOWN :: 4;
79 | }
80 |
81 | PGVerbosity :: enum u32 {
82 | PQERRORS_TERSE :: 0;
83 | PQERRORS_DEFAULT :: 1;
84 | PQERRORS_VERBOSE :: 2;
85 | }
86 |
87 | PGContextVisibility :: enum u32 {
88 | PQSHOW_CONTEXT_NEVER :: 0;
89 | PQSHOW_CONTEXT_ERRORS :: 1;
90 | PQSHOW_CONTEXT_ALWAYS :: 2;
91 | }
92 |
93 | /*
94 | * PGPing - The ordering of this enum should not be altered because the
95 | * values are exposed externally via pg_isready.
96 | */
97 | PGPing :: enum u32 {
98 | PQPING_OK :: 0;
99 | PQPING_REJECT :: 1;
100 | PQPING_NO_RESPONSE :: 2;
101 | PQPING_NO_ATTEMPT :: 3;
102 | }
103 |
104 | pg_conn :: struct {}
105 | /* PGconn encapsulates a connection to the backend.
106 | * The contents of this struct are not supposed to be known to applications.
107 | */
108 | PGconn :: pg_conn;
109 |
110 | pg_result :: struct {}
111 | /* PGresult encapsulates the result of a query (or more precisely, of a single
112 | * SQL command --- a query string given to PQsendQuery can contain multiple
113 | * commands and thus return multiple PGresult objects).
114 | * The contents of this struct are not supposed to be known to applications.
115 | */
116 | PGresult :: pg_result;
117 |
118 | pg_cancel :: struct {}
119 | /* PGcancel encapsulates the information needed to cancel a running
120 | * query on an existing connection.
121 | * The contents of this struct are not supposed to be known to applications.
122 | */
123 | PGcancel :: pg_cancel;
124 |
125 | /* PGnotify represents the occurrence of a NOTIFY message.
126 | * Ideally this would be an opaque typedef, but it's so simple that it's
127 | * unlikely to change.
128 | * NOTE: in Postgres 6.4 and later, the be_pid is the notifying backend's,
129 | * whereas in earlier versions it was always your own backend's PID.
130 | */
131 | pgNotify :: struct {
132 | relname: *u8; /* notification condition name */
133 | be_pid: s32; /* process ID of notifying server process */
134 | extra: *u8; /* notification parameter */
135 |
136 | next: *pgNotify; /* list link */
137 | }
138 |
139 | /* PGnotify represents the occurrence of a NOTIFY message.
140 | * Ideally this would be an opaque typedef, but it's so simple that it's
141 | * unlikely to change.
142 | * NOTE: in Postgres 6.4 and later, the be_pid is the notifying backend's,
143 | * whereas in earlier versions it was always your own backend's PID.
144 | */
145 | PGnotify :: pgNotify;
146 |
147 | /* Function types for notice-handling callbacks */
148 | PQnoticeReceiver :: #type (arg: *void, res: *PGresult) -> void #c_call;
149 | PQnoticeProcessor :: #type (arg: *void, message: *u8) -> void #c_call;
150 |
151 | /* Print options for PQprint() */
152 | pqbool :: u8;
153 |
154 | _PQprintOpt :: struct {
155 | header: pqbool; /* print output field headings and row count */
156 | align: pqbool; /* fill align the fields */
157 | standard: pqbool; /* old brain dead format */
158 | html3: pqbool; /* output html tables */
159 | expanded: pqbool; /* expand tables */
160 | pager: pqbool; /* use pager for output if needed */
161 | fieldSep: *u8; /* field separator */
162 | tableOpt: *u8; /* insert to HTML
*/
163 | caption: *u8; /* HTML */
164 | /* null terminated array of replacement field
165 | * names */
166 | fieldName: **u8;
167 | }
168 |
169 | PQprintOpt :: _PQprintOpt;
170 |
171 | /* ----------------
172 | * Structure for the conninfo parameter definitions returned by PQconndefaults
173 | * or PQconninfoParse.
174 | *
175 | * All fields except "val" point at static strings which must not be altered.
176 | * "val" is either NULL or a malloc'd current-value string. PQconninfoFree()
177 | * will release both the val strings and the PQconninfoOption array itself.
178 | * ----------------
179 | */
180 | _PQconninfoOption :: struct {
181 | keyword: *u8; /* The keyword of the option */
182 | envvar: *u8; /* Fallback environment variable name */
183 | compiled: *u8; /* Fallback compiled in default value */
184 | val: *u8; /* Option's current value, or NULL */
185 | label: *u8; /* Label for field in connect dialog */
186 | /* Indicates how to display this field in a
187 | * connect dialog. Values are: "" Display
188 | * entered value as is "*" Password field -
189 | * hide value "D" Debug option - don't show
190 | * by default */
191 | dispchar: *u8;
192 |
193 | dispsize: s32; /* Field size in characters for dialog */
194 | }
195 |
196 | /* ----------------
197 | * Structure for the conninfo parameter definitions returned by PQconndefaults
198 | * or PQconninfoParse.
199 | *
200 | * All fields except "val" point at static strings which must not be altered.
201 | * "val" is either NULL or a malloc'd current-value string. PQconninfoFree()
202 | * will release both the val strings and the PQconninfoOption array itself.
203 | * ----------------
204 | */
205 | PQconninfoOption :: _PQconninfoOption;
206 |
207 | /* ----------------
208 | * PQArgBlock -- structure for PQfn() arguments
209 | * ----------------
210 | */
211 | PQArgBlock :: struct {
212 | len: s32;
213 | isint: s32;
214 | u: union {
215 | ptr: *s32; /* can't use void (dec compiler barfs) */
216 | integer: s32;
217 | };
218 | }
219 |
220 | /* ----------------
221 | * PGresAttDesc -- Data about a single attribute (column) of a query result
222 | * ----------------
223 | */
224 | pgresAttDesc :: struct {
225 | name: *u8; /* column name */
226 | tableid: Oid; /* source table, if known */
227 | columnid: s32; /* source column, if known */
228 | format: s32; /* format code for value (text/binary) */
229 | typid: Oid; /* type id */
230 | typlen: s32; /* type size */
231 | atttypmod: s32; /* type-specific modifier info */
232 | }
233 |
234 | /* ----------------
235 | * PGresAttDesc -- Data about a single attribute (column) of a query result
236 | * ----------------
237 | */
238 | PGresAttDesc :: pgresAttDesc;
239 |
240 | /* make a new client connection to the backend */
241 | /* Asynchronous (non-blocking) */
242 | PQconnectStart :: (conninfo: *u8) -> *PGconn #foreign libpq;
243 | PQconnectStartParams :: (keywords: **u8, values: **u8, expand_dbname: s32) -> *PGconn #foreign libpq;
244 |
245 | PQconnectPoll :: (conn: *PGconn) -> PostgresPollingStatusType #foreign libpq;
246 |
247 | /* Synchronous (blocking) */
248 | PQconnectdb :: (conninfo: *u8) -> *PGconn #foreign libpq;
249 | PQconnectdbParams :: (keywords: **u8, values: **u8, expand_dbname: s32) -> *PGconn #foreign libpq;
250 |
251 | PQsetdbLogin :: (pghost: *u8, pgport: *u8, pgoptions: *u8, pgtty: *u8, dbName: *u8, login: *u8, pwd: *u8) -> *PGconn #foreign libpq;
252 |
253 | /* close the current connection and free the PGconn data structure */
254 | PQfinish :: (conn: *PGconn) -> void #foreign libpq;
255 |
256 | /* get info about connection options known to PQconnectdb */
257 | PQconndefaults :: () -> *PQconninfoOption #foreign libpq;
258 |
259 | /* parse connection options in same way as PQconnectdb */
260 | PQconninfoParse :: (conninfo: *u8, errmsg: **u8) -> *PQconninfoOption #foreign libpq;
261 |
262 | /* return the connection options used by a live connection */
263 | PQconninfo :: (conn: *PGconn) -> *PQconninfoOption #foreign libpq;
264 |
265 | /* free the data structure returned by PQconndefaults() or PQconninfoParse() */
266 | PQconninfoFree :: (connOptions: *PQconninfoOption) -> void #foreign libpq;
267 |
268 | /*
269 | * close the current connection and restablish a new one with the same
270 | * parameters
271 | */
272 | /* Asynchronous (non-blocking) */
273 | PQresetStart :: (conn: *PGconn) -> s32 #foreign libpq;
274 | PQresetPoll :: (conn: *PGconn) -> PostgresPollingStatusType #foreign libpq;
275 |
276 | /* Synchronous (blocking) */
277 | PQreset :: (conn: *PGconn) -> void #foreign libpq;
278 |
279 | /* request a cancel structure */
280 | PQgetCancel :: (conn: *PGconn) -> *PGcancel #foreign libpq;
281 |
282 | /* free a cancel structure */
283 | PQfreeCancel :: (cancel: *PGcancel) -> void #foreign libpq;
284 |
285 | /* issue a cancel request */
286 | PQcancel :: (cancel: *PGcancel, errbuf: *u8, errbufsize: s32) -> s32 #foreign libpq;
287 |
288 | /* backwards compatible version of PQcancel; not thread-safe */
289 | PQrequestCancel :: (conn: *PGconn) -> s32 #foreign libpq;
290 |
291 | /* Accessor functions for PGconn objects */
292 | PQdb :: (conn: *PGconn) -> *u8 #foreign libpq;
293 | PQuser :: (conn: *PGconn) -> *u8 #foreign libpq;
294 | PQpass :: (conn: *PGconn) -> *u8 #foreign libpq;
295 | PQhost :: (conn: *PGconn) -> *u8 #foreign libpq;
296 | PQport :: (conn: *PGconn) -> *u8 #foreign libpq;
297 | PQtty :: (conn: *PGconn) -> *u8 #foreign libpq;
298 | PQoptions :: (conn: *PGconn) -> *u8 #foreign libpq;
299 | PQstatus :: (conn: *PGconn) -> ConnStatusType #foreign libpq;
300 | PQtransactionStatus :: (conn: *PGconn) -> PGTransactionStatusType #foreign libpq;
301 | PQparameterStatus :: (conn: *PGconn, paramName: *u8) -> *u8 #foreign libpq;
302 |
303 | PQprotocolVersion :: (conn: *PGconn) -> s32 #foreign libpq;
304 | PQserverVersion :: (conn: *PGconn) -> s32 #foreign libpq;
305 | PQerrorMessage :: (conn: *PGconn) -> *u8 #foreign libpq;
306 | PQsocket :: (conn: *PGconn) -> s32 #foreign libpq;
307 | PQbackendPID :: (conn: *PGconn) -> s32 #foreign libpq;
308 | PQconnectionNeedsPassword :: (conn: *PGconn) -> s32 #foreign libpq;
309 | PQconnectionUsedPassword :: (conn: *PGconn) -> s32 #foreign libpq;
310 | PQclientEncoding :: (conn: *PGconn) -> s32 #foreign libpq;
311 | PQsetClientEncoding :: (conn: *PGconn, encoding: *u8) -> s32 #foreign libpq;
312 |
313 | /* SSL information functions */
314 | PQsslInUse :: (conn: *PGconn) -> s32 #foreign libpq;
315 | PQsslStruct :: (conn: *PGconn, struct_name: *u8) -> *void #foreign libpq;
316 | PQsslAttribute :: (conn: *PGconn, attribute_name: *u8) -> *u8 #foreign libpq;
317 | PQsslAttributeNames :: (conn: *PGconn) -> **u8 #foreign libpq;
318 |
319 | /* Get the OpenSSL structure associated with a connection. Returns NULL for
320 | * unencrypted connections or if any other TLS library is in use. */
321 | PQgetssl :: (conn: *PGconn) -> *void #foreign libpq;
322 |
323 | /* Tell libpq whether it needs to initialize OpenSSL */
324 | PQinitSSL :: (do_init: s32) -> void #foreign libpq;
325 |
326 | /* More detailed way to tell libpq whether it needs to initialize OpenSSL */
327 | PQinitOpenSSL :: (do_ssl: s32, do_crypto: s32) -> void #foreign libpq;
328 |
329 | /* Set verbosity for PQerrorMessage and PQresultErrorMessage */
330 | PQsetErrorVerbosity :: (conn: *PGconn, verbosity: PGVerbosity) -> PGVerbosity #foreign libpq;
331 |
332 | /* Set CONTEXT visibility for PQerrorMessage and PQresultErrorMessage */
333 | PQsetErrorContextVisibility :: (conn: *PGconn, show_context: PGContextVisibility) -> PGContextVisibility #foreign libpq;
334 |
335 | /* Enable/disable tracing */
336 | PQtrace :: (conn: *PGconn, debug_port: *FILE) -> void #foreign libpq;
337 | PQuntrace :: (conn: *PGconn) -> void #foreign libpq;
338 |
339 | /* Override default notice handling routines */
340 | PQsetNoticeReceiver :: (conn: *PGconn, proc: PQnoticeReceiver, arg: *void) -> PQnoticeReceiver #foreign libpq;
341 |
342 | PQsetNoticeProcessor :: (conn: *PGconn, proc: PQnoticeProcessor, arg: *void) -> PQnoticeProcessor #foreign libpq;
343 |
344 | /*
345 | * Used to set callback that prevents concurrent access to
346 | * non-thread safe functions that libpq needs.
347 | * The default implementation uses a libpq internal mutex.
348 | * Only required for multithreaded apps that use kerberos
349 | * both within their app and for postgresql connections.
350 | */
351 | pgthreadlock_t :: #type (acquire: s32) -> void #c_call;
352 |
353 | PQregisterThreadLock :: (newhandler: pgthreadlock_t) -> pgthreadlock_t #foreign libpq;
354 |
355 | /* Simple synchronous query */
356 | PQexec :: (conn: *PGconn, query: *u8) -> *PGresult #foreign libpq;
357 | PQexecParams :: (conn: *PGconn, command: *u8, nParams: s32, paramTypes: *Oid, paramValues: **u8, paramLengths: *s32, paramFormats: *s32, resultFormat: s32) -> *PGresult #foreign libpq;
358 |
359 | PQprepare :: (conn: *PGconn, stmtName: *u8, query: *u8, nParams: s32, paramTypes: *Oid) -> *PGresult #foreign libpq;
360 |
361 | PQexecPrepared :: (conn: *PGconn, stmtName: *u8, nParams: s32, paramValues: **u8, paramLengths: *s32, paramFormats: *s32, resultFormat: s32) -> *PGresult #foreign libpq;
362 |
363 | /* Interface for multiple-result or asynchronous queries */
364 | PQsendQuery :: (conn: *PGconn, query: *u8) -> s32 #foreign libpq;
365 | PQsendQueryParams :: (conn: *PGconn, command: *u8, nParams: s32, paramTypes: *Oid, paramValues: **u8, paramLengths: *s32, paramFormats: *s32, resultFormat: s32) -> s32 #foreign libpq;
366 |
367 | PQsendPrepare :: (conn: *PGconn, stmtName: *u8, query: *u8, nParams: s32, paramTypes: *Oid) -> s32 #foreign libpq;
368 |
369 | PQsendQueryPrepared :: (conn: *PGconn, stmtName: *u8, nParams: s32, paramValues: **u8, paramLengths: *s32, paramFormats: *s32, resultFormat: s32) -> s32 #foreign libpq;
370 |
371 | PQsetSingleRowMode :: (conn: *PGconn) -> s32 #foreign libpq;
372 | PQgetResult :: (conn: *PGconn) -> *PGresult #foreign libpq;
373 |
374 | /* Routines for managing an asynchronous query */
375 | PQisBusy :: (conn: *PGconn) -> s32 #foreign libpq;
376 | PQconsumeInput :: (conn: *PGconn) -> s32 #foreign libpq;
377 |
378 | /* LISTEN/NOTIFY support */
379 | PQnotifies :: (conn: *PGconn) -> *PGnotify #foreign libpq;
380 |
381 | /* Routines for copy in/out */
382 | PQputCopyData :: (conn: *PGconn, buffer: *u8, nbytes: s32) -> s32 #foreign libpq;
383 | PQputCopyEnd :: (conn: *PGconn, errormsg: *u8) -> s32 #foreign libpq;
384 | PQgetCopyData :: (conn: *PGconn, buffer: **u8, async: s32) -> s32 #foreign libpq;
385 |
386 | /* Deprecated routines for copy in/out */
387 | PQgetline :: (conn: *PGconn, _string: *u8, length: s32) -> s32 #foreign libpq;
388 | PQputline :: (conn: *PGconn, _string: *u8) -> s32 #foreign libpq;
389 | PQgetlineAsync :: (conn: *PGconn, buffer: *u8, bufsize: s32) -> s32 #foreign libpq;
390 | PQputnbytes :: (conn: *PGconn, buffer: *u8, nbytes: s32) -> s32 #foreign libpq;
391 | PQendcopy :: (conn: *PGconn) -> s32 #foreign libpq;
392 |
393 | /* Set blocking/nonblocking connection to the backend */
394 | PQsetnonblocking :: (conn: *PGconn, arg: s32) -> s32 #foreign libpq;
395 | PQisnonblocking :: (conn: *PGconn) -> s32 #foreign libpq;
396 | PQisthreadsafe :: () -> s32 #foreign libpq;
397 | PQping :: (conninfo: *u8) -> PGPing #foreign libpq;
398 | PQpingParams :: (keywords: **u8, values: **u8, expand_dbname: s32) -> PGPing #foreign libpq;
399 |
400 | /* Force the write buffer to be written (or at least try) */
401 | PQflush :: (conn: *PGconn) -> s32 #foreign libpq;
402 |
403 | /*
404 | * "Fast path" interface --- not really recommended for application
405 | * use
406 | */
407 | PQfn :: (conn: *PGconn, fnid: s32, result_buf: *s32, result_len: *s32, result_is_int: s32, args: *PQArgBlock, nargs: s32) -> *PGresult #foreign libpq;
408 |
409 | /* Accessor functions for PGresult objects */
410 | PQresultStatus :: (res: *PGresult) -> ExecStatusType #foreign libpq;
411 | PQresStatus :: (status: ExecStatusType) -> *u8 #foreign libpq;
412 | PQresultErrorMessage :: (res: *PGresult) -> *u8 #foreign libpq;
413 | PQresultVerboseErrorMessage :: (res: *PGresult, verbosity: PGVerbosity, show_context: PGContextVisibility) -> *u8 #foreign libpq;
414 |
415 | PQresultErrorField :: (res: *PGresult, fieldcode: s32) -> *u8 #foreign libpq;
416 | PQntuples :: (res: *PGresult) -> s32 #foreign libpq;
417 | PQnfields :: (res: *PGresult) -> s32 #foreign libpq;
418 | PQbinaryTuples :: (res: *PGresult) -> s32 #foreign libpq;
419 | PQfname :: (res: *PGresult, field_num: s32) -> *u8 #foreign libpq;
420 | PQfnumber :: (res: *PGresult, field_name: *u8) -> s32 #foreign libpq;
421 | PQftable :: (res: *PGresult, field_num: s32) -> Oid #foreign libpq;
422 | PQftablecol :: (res: *PGresult, field_num: s32) -> s32 #foreign libpq;
423 | PQfformat :: (res: *PGresult, field_num: s32) -> s32 #foreign libpq;
424 | PQftype :: (res: *PGresult, field_num: s32) -> Oid #foreign libpq;
425 | PQfsize :: (res: *PGresult, field_num: s32) -> s32 #foreign libpq;
426 | PQfmod :: (res: *PGresult, field_num: s32) -> s32 #foreign libpq;
427 | PQcmdStatus :: (res: *PGresult) -> *u8 #foreign libpq;
428 | PQoidStatus :: (res: *PGresult) -> *u8 #foreign libpq;
429 | PQoidValue :: (res: *PGresult) -> Oid #foreign libpq;
430 | PQcmdTuples :: (res: *PGresult) -> *u8 #foreign libpq;
431 | PQgetvalue :: (res: *PGresult, tup_num: s32, field_num: s32) -> *u8 #foreign libpq;
432 | PQgetlength :: (res: *PGresult, tup_num: s32, field_num: s32) -> s32 #foreign libpq;
433 | PQgetisnull :: (res: *PGresult, tup_num: s32, field_num: s32) -> s32 #foreign libpq;
434 | PQnparams :: (res: *PGresult) -> s32 #foreign libpq;
435 | PQparamtype :: (res: *PGresult, param_num: s32) -> Oid #foreign libpq;
436 |
437 | /* Describe prepared statements and portals */
438 | PQdescribePrepared :: (conn: *PGconn, stmt: *u8) -> *PGresult #foreign libpq;
439 | PQdescribePortal :: (conn: *PGconn, portal: *u8) -> *PGresult #foreign libpq;
440 | PQsendDescribePrepared :: (conn: *PGconn, stmt: *u8) -> s32 #foreign libpq;
441 | PQsendDescribePortal :: (conn: *PGconn, portal: *u8) -> s32 #foreign libpq;
442 |
443 | /* Delete a PGresult */
444 | PQclear :: (res: *PGresult) -> void #foreign libpq;
445 |
446 | /* For freeing other alloc'd results, such as PGnotify structs */
447 | PQfreemem :: (ptr: *void) -> void #foreign libpq;
448 |
449 | /* Create and manipulate PGresults */
450 | PQmakeEmptyPGresult :: (conn: *PGconn, status: ExecStatusType) -> *PGresult #foreign libpq;
451 | PQcopyResult :: (src: *PGresult, flags: s32) -> *PGresult #foreign libpq;
452 | PQsetResultAttrs :: (res: *PGresult, numAttributes: s32, attDescs: *PGresAttDesc) -> s32 #foreign libpq;
453 | PQresultAlloc :: (res: *PGresult, nBytes: size_t) -> *void #foreign libpq;
454 | PQsetvalue :: (res: *PGresult, tup_num: s32, field_num: s32, value: *u8, len: s32) -> s32 #foreign libpq;
455 |
456 | /* Quoting strings before inclusion in queries. */
457 | PQescapeStringConn :: (conn: *PGconn, to: *u8, from: *u8, length: size_t, error: *s32) -> size_t #foreign libpq;
458 |
459 | PQescapeLiteral :: (conn: *PGconn, str: *u8, len: size_t) -> *u8 #foreign libpq;
460 | PQescapeIdentifier :: (conn: *PGconn, str: *u8, len: size_t) -> *u8 #foreign libpq;
461 | PQescapeByteaConn :: (conn: *PGconn, from: *u8, from_length: size_t, to_length: *size_t) -> *u8 #foreign libpq;
462 |
463 | PQunescapeBytea :: (strtext: *u8, retbuflen: *size_t) -> *u8 #foreign libpq;
464 |
465 | /* These forms are deprecated! */
466 | PQescapeString :: (to: *u8, from: *u8, length: size_t) -> size_t #foreign libpq;
467 | PQescapeBytea :: (from: *u8, from_length: size_t, to_length: *size_t) -> *u8 #foreign libpq;
468 |
469 | /* === in fe-print.c === */
470 | PQprint :: (fout: *FILE, res: *PGresult, ps: *PQprintOpt) -> void #foreign libpq;
471 |
472 | /*
473 | * really old printing routines
474 | */
475 | PQdisplayTuples :: (res: *PGresult, fp: *FILE, fillAlign: s32, fieldSep: *u8, printHeader: s32, quiet: s32) -> void #foreign libpq;
476 |
477 | PQprintTuples :: (res: *PGresult, fout: *FILE, printAttName: s32, terseOutput: s32, width: s32) -> void #foreign libpq;
478 |
479 | /* Large-object access routines */
480 | lo_open :: (conn: *PGconn, lobjId: Oid, mode: s32) -> s32 #foreign libpq;
481 | lo_close :: (conn: *PGconn, fd: s32) -> s32 #foreign libpq;
482 | lo_read :: (conn: *PGconn, fd: s32, buf: *u8, len: size_t) -> s32 #foreign libpq;
483 | lo_write :: (conn: *PGconn, fd: s32, buf: *u8, len: size_t) -> s32 #foreign libpq;
484 | lo_lseek :: (conn: *PGconn, fd: s32, offset: s32, whence: s32) -> s32 #foreign libpq;
485 | lo_lseek64 :: (conn: *PGconn, fd: s32, offset: pg_int64, whence: s32) -> pg_int64 #foreign libpq;
486 | lo_creat :: (conn: *PGconn, mode: s32) -> Oid #foreign libpq;
487 | lo_create :: (conn: *PGconn, lobjId: Oid) -> Oid #foreign libpq;
488 | lo_tell :: (conn: *PGconn, fd: s32) -> s32 #foreign libpq;
489 | lo_tell64 :: (conn: *PGconn, fd: s32) -> pg_int64 #foreign libpq;
490 | lo_truncate :: (conn: *PGconn, fd: s32, len: size_t) -> s32 #foreign libpq;
491 | lo_truncate64 :: (conn: *PGconn, fd: s32, len: pg_int64) -> s32 #foreign libpq;
492 | lo_unlink :: (conn: *PGconn, lobjId: Oid) -> s32 #foreign libpq;
493 | lo_import :: (conn: *PGconn, filename: *u8) -> Oid #foreign libpq;
494 | lo_import_with_oid :: (conn: *PGconn, filename: *u8, lobjId: Oid) -> Oid #foreign libpq;
495 | lo_export :: (conn: *PGconn, lobjId: Oid, filename: *u8) -> s32 #foreign libpq;
496 |
497 | /* Get the version of the libpq library in use */
498 | PQlibVersion :: () -> s32 #foreign libpq;
499 |
500 | /* Determine length of multibyte encoded char at *s */
501 | PQmblen :: (s: *u8, encoding: s32) -> s32 #foreign libpq;
502 |
503 | /* Determine display length of multibyte encoded char at *s */
504 | PQdsplen :: (s: *u8, encoding: s32) -> s32 #foreign libpq;
505 |
506 | /* Get encoding id from environment variable PGCLIENTENCODING */
507 | PQenv2encoding :: () -> s32 #foreign libpq;
508 |
509 | /* === in fe-auth.c === */
510 | PQencryptPassword :: (passwd: *u8, user: *u8) -> *u8 #foreign libpq;
511 | PQencryptPasswordConn :: (conn: *PGconn, passwd: *u8, user: *u8, algorithm: *u8) -> *u8 #foreign libpq;
512 |
513 | /* === in encnames.c === */
514 | pg_char_to_encoding :: (name: *u8) -> s32 #foreign libpq;
515 | pg_encoding_to_char :: (encoding: s32) -> *u8 #foreign libpq;
516 | pg_valid_server_encoding_id :: (encoding: s32) -> s32 #foreign libpq;
517 |
518 | #scope_file
519 |
520 |
521 | libpq :: #system_library "libpq";
522 |
523 |
--------------------------------------------------------------------------------
/bindings/windows.jai:
--------------------------------------------------------------------------------
1 | //
2 | // This file was auto-generated using the following command:
3 | //
4 | // jai generate.jai
5 | //
6 |
7 |
8 |
9 | InvalidOid :: cast(Oid) 0;
10 |
11 | LIBPQ_HAS_PIPELINING :: 1;
12 |
13 | LIBPQ_HAS_TRACE_FLAGS :: 1;
14 |
15 | LIBPQ_HAS_SSL_LIBRARY_DETECTION :: 1;
16 |
17 | PG_COPYRES_ATTRS :: 0x01;
18 | PG_COPYRES_TUPLES :: 0x02;
19 | PG_COPYRES_EVENTS :: 0x04;
20 | PG_COPYRES_NOTICEHOOKS :: 0x08;
21 |
22 | PQTRACE_SUPPRESS_TIMESTAMPS :: 1<<0;
23 |
24 | PQTRACE_REGRESS_MODE :: 1<<1;
25 |
26 | PQ_QUERY_PARAM_MAX_LIMIT :: 65535;
27 |
28 | PQnoPasswordSupplied :: "fe_sendauth: no password supplied\n";
29 |
30 | /*
31 | * Object ID is a fundamental type in Postgres.
32 | */
33 | Oid :: u32;
34 |
35 | /* Define a signed 64-bit integer type for use in client API declarations. */
36 | pg_int64 :: s64;
37 |
38 | /*
39 | * Although it is okay to add to these lists, values which become unused
40 | * should never be removed, nor should constants be redefined - that would
41 | * break compatibility with existing code.
42 | */
43 | ConnStatusType :: enum s32 {
44 | CONNECTION_OK :: 0;
45 | CONNECTION_BAD :: 1;
46 |
47 | CONNECTION_STARTED :: 2;
48 | CONNECTION_MADE :: 3;
49 | CONNECTION_AWAITING_RESPONSE :: 4;
50 |
51 | CONNECTION_AUTH_OK :: 5;
52 |
53 | CONNECTION_SETENV :: 6;
54 | CONNECTION_SSL_STARTUP :: 7;
55 | CONNECTION_NEEDED :: 8;
56 | CONNECTION_CHECK_WRITABLE :: 9;
57 | CONNECTION_CONSUME :: 10;
58 | CONNECTION_GSS_STARTUP :: 11;
59 | CONNECTION_CHECK_TARGET :: 12;
60 | CONNECTION_CHECK_STANDBY :: 13;
61 | }
62 |
63 | PostgresPollingStatusType :: enum s32 {
64 | PGRES_POLLING_FAILED :: 0;
65 | PGRES_POLLING_READING :: 1;
66 | PGRES_POLLING_WRITING :: 2;
67 | PGRES_POLLING_OK :: 3;
68 | PGRES_POLLING_ACTIVE :: 4;
69 | }
70 |
71 | ExecStatusType :: enum s32 {
72 | PGRES_EMPTY_QUERY :: 0;
73 | PGRES_COMMAND_OK :: 1;
74 |
75 | PGRES_TUPLES_OK :: 2;
76 |
77 | PGRES_COPY_OUT :: 3;
78 | PGRES_COPY_IN :: 4;
79 | PGRES_BAD_RESPONSE :: 5;
80 |
81 | PGRES_NONFATAL_ERROR :: 6;
82 | PGRES_FATAL_ERROR :: 7;
83 | PGRES_COPY_BOTH :: 8;
84 | PGRES_SINGLE_TUPLE :: 9;
85 | PGRES_PIPELINE_SYNC :: 10;
86 | PGRES_PIPELINE_ABORTED :: 11;
87 | }
88 |
89 | PGTransactionStatusType :: enum s32 {
90 | PQTRANS_IDLE :: 0;
91 | PQTRANS_ACTIVE :: 1;
92 | PQTRANS_INTRANS :: 2;
93 | PQTRANS_INERROR :: 3;
94 | PQTRANS_UNKNOWN :: 4;
95 | }
96 |
97 | PGVerbosity :: enum s32 {
98 | PQERRORS_TERSE :: 0;
99 | PQERRORS_DEFAULT :: 1;
100 | PQERRORS_VERBOSE :: 2;
101 | PQERRORS_SQLSTATE :: 3;
102 | }
103 |
104 | PGContextVisibility :: enum s32 {
105 | PQSHOW_CONTEXT_NEVER :: 0;
106 | PQSHOW_CONTEXT_ERRORS :: 1;
107 | PQSHOW_CONTEXT_ALWAYS :: 2;
108 | }
109 |
110 | /*
111 | * PGPing - The ordering of this enum should not be altered because the
112 | * values are exposed externally via pg_isready.
113 | */
114 | PGPing :: enum s32 {
115 | PQPING_OK :: 0;
116 | PQPING_REJECT :: 1;
117 | PQPING_NO_RESPONSE :: 2;
118 | PQPING_NO_ATTEMPT :: 3;
119 | }
120 |
121 | /*
122 | * PGpipelineStatus - Current status of pipeline mode
123 | */
124 | PGpipelineStatus :: enum s32 {
125 | PQ_PIPELINE_OFF :: 0;
126 | PQ_PIPELINE_ON :: 1;
127 | PQ_PIPELINE_ABORTED :: 2;
128 | }
129 |
130 | pg_conn :: struct {}
131 | /* PGconn encapsulates a connection to the backend.
132 | * The contents of this struct are not supposed to be known to applications.
133 | */
134 | PGconn :: pg_conn;
135 |
136 | pg_result :: struct {}
137 | /* PGresult encapsulates the result of a query (or more precisely, of a single
138 | * SQL command --- a query string given to PQsendQuery can contain multiple
139 | * commands and thus return multiple PGresult objects).
140 | * The contents of this struct are not supposed to be known to applications.
141 | */
142 | PGresult :: pg_result;
143 |
144 | pg_cancel :: struct {}
145 | /* PGcancel encapsulates the information needed to cancel a running
146 | * query on an existing connection.
147 | * The contents of this struct are not supposed to be known to applications.
148 | */
149 | PGcancel :: pg_cancel;
150 |
151 | /* PGnotify represents the occurrence of a NOTIFY message.
152 | * Ideally this would be an opaque typedef, but it's so simple that it's
153 | * unlikely to change.
154 | * NOTE: in Postgres 6.4 and later, the be_pid is the notifying backend's,
155 | * whereas in earlier versions it was always your own backend's PID.
156 | */
157 | pgNotify :: struct {
158 | relname: *u8; /* notification condition name */
159 | be_pid: s32; /* process ID of notifying server process */
160 | extra: *u8; /* notification parameter */
161 |
162 | next: *pgNotify; /* list link */
163 | }
164 |
165 | /* PGnotify represents the occurrence of a NOTIFY message.
166 | * Ideally this would be an opaque typedef, but it's so simple that it's
167 | * unlikely to change.
168 | * NOTE: in Postgres 6.4 and later, the be_pid is the notifying backend's,
169 | * whereas in earlier versions it was always your own backend's PID.
170 | */
171 | PGnotify :: pgNotify;
172 |
173 | /* Function types for notice-handling callbacks */
174 | PQnoticeReceiver :: #type (arg: *void, res: *PGresult) -> void #c_call;
175 | PQnoticeProcessor :: #type (arg: *void, message: *u8) -> void #c_call;
176 |
177 | /* Print options for PQprint() */
178 | pqbool :: u8;
179 |
180 | _PQprintOpt :: struct {
181 | header: pqbool; /* print output field headings and row count */
182 | align: pqbool; /* fill align the fields */
183 | standard: pqbool; /* old brain dead format */
184 | html3: pqbool; /* output html tables */
185 | expanded: pqbool; /* expand tables */
186 | pager: pqbool; /* use pager for output if needed */
187 | fieldSep: *u8; /* field separator */
188 | tableOpt: *u8; /* insert to HTML */
189 | caption: *u8; /* HTML */
190 | /* null terminated array of replacement field
191 | * names */
192 | fieldName: **u8;
193 | }
194 |
195 | PQprintOpt :: _PQprintOpt;
196 |
197 | /* ----------------
198 | * Structure for the conninfo parameter definitions returned by PQconndefaults
199 | * or PQconninfoParse.
200 | *
201 | * All fields except "val" point at static strings which must not be altered.
202 | * "val" is either NULL or a malloc'd current-value string. PQconninfoFree()
203 | * will release both the val strings and the PQconninfoOption array itself.
204 | * ----------------
205 | */
206 | _PQconninfoOption :: struct {
207 | keyword: *u8; /* The keyword of the option */
208 | envvar: *u8; /* Fallback environment variable name */
209 | compiled: *u8; /* Fallback compiled in default value */
210 | val: *u8; /* Option's current value, or NULL */
211 | label: *u8; /* Label for field in connect dialog */
212 | /* Indicates how to display this field in a
213 | * connect dialog. Values are: "" Display
214 | * entered value as is "*" Password field -
215 | * hide value "D" Debug option - don't show
216 | * by default */
217 | dispchar: *u8;
218 |
219 | dispsize: s32; /* Field size in characters for dialog */
220 | }
221 |
222 | /* ----------------
223 | * Structure for the conninfo parameter definitions returned by PQconndefaults
224 | * or PQconninfoParse.
225 | *
226 | * All fields except "val" point at static strings which must not be altered.
227 | * "val" is either NULL or a malloc'd current-value string. PQconninfoFree()
228 | * will release both the val strings and the PQconninfoOption array itself.
229 | * ----------------
230 | */
231 | PQconninfoOption :: _PQconninfoOption;
232 |
233 | /* ----------------
234 | * PQArgBlock -- structure for PQfn() arguments
235 | * ----------------
236 | */
237 | PQArgBlock :: struct {
238 | len: s32;
239 | isint: s32;
240 | u: union {
241 | ptr: *s32; /* can't use void (dec compiler barfs) */
242 | integer: s32;
243 | };
244 | }
245 |
246 | /* ----------------
247 | * PGresAttDesc -- Data about a single attribute (column) of a query result
248 | * ----------------
249 | */
250 | pgresAttDesc :: struct {
251 | name: *u8; /* column name */
252 | tableid: Oid; /* source table, if known */
253 | columnid: s32; /* source column, if known */
254 | format: s32; /* format code for value (text/binary) */
255 | typid: Oid; /* type id */
256 | typlen: s32; /* type size */
257 | atttypmod: s32; /* type-specific modifier info */
258 | }
259 |
260 | /* ----------------
261 | * PGresAttDesc -- Data about a single attribute (column) of a query result
262 | * ----------------
263 | */
264 | PGresAttDesc :: pgresAttDesc;
265 |
266 | /* make a new client connection to the backend */
267 | /* Asynchronous (non-blocking) */
268 | PQconnectStart :: (conninfo: *u8) -> *PGconn #foreign libpq;
269 | PQconnectStartParams :: (keywords: **u8, values: **u8, expand_dbname: s32) -> *PGconn #foreign libpq;
270 |
271 | PQconnectPoll :: (conn: *PGconn) -> PostgresPollingStatusType #foreign libpq;
272 |
273 | /* Synchronous (blocking) */
274 | PQconnectdb :: (conninfo: *u8) -> *PGconn #foreign libpq;
275 | PQconnectdbParams :: (keywords: **u8, values: **u8, expand_dbname: s32) -> *PGconn #foreign libpq;
276 |
277 | PQsetdbLogin :: (pghost: *u8, pgport: *u8, pgoptions: *u8, pgtty: *u8, dbName: *u8, login: *u8, pwd: *u8) -> *PGconn #foreign libpq;
278 |
279 | /* close the current connection and free the PGconn data structure */
280 | PQfinish :: (conn: *PGconn) -> void #foreign libpq;
281 |
282 | /* get info about connection options known to PQconnectdb */
283 | PQconndefaults :: () -> *PQconninfoOption #foreign libpq;
284 |
285 | /* parse connection options in same way as PQconnectdb */
286 | PQconninfoParse :: (conninfo: *u8, errmsg: **u8) -> *PQconninfoOption #foreign libpq;
287 |
288 | /* return the connection options used by a live connection */
289 | PQconninfo :: (conn: *PGconn) -> *PQconninfoOption #foreign libpq;
290 |
291 | /* free the data structure returned by PQconndefaults() or PQconninfoParse() */
292 | PQconninfoFree :: (connOptions: *PQconninfoOption) -> void #foreign libpq;
293 |
294 | /*
295 | * close the current connection and reestablish a new one with the same
296 | * parameters
297 | */
298 | /* Asynchronous (non-blocking) */
299 | PQresetStart :: (conn: *PGconn) -> s32 #foreign libpq;
300 | PQresetPoll :: (conn: *PGconn) -> PostgresPollingStatusType #foreign libpq;
301 |
302 | /* Synchronous (blocking) */
303 | PQreset :: (conn: *PGconn) -> void #foreign libpq;
304 |
305 | /* request a cancel structure */
306 | PQgetCancel :: (conn: *PGconn) -> *PGcancel #foreign libpq;
307 |
308 | /* free a cancel structure */
309 | PQfreeCancel :: (cancel: *PGcancel) -> void #foreign libpq;
310 |
311 | /* issue a cancel request */
312 | PQcancel :: (cancel: *PGcancel, errbuf: *u8, errbufsize: s32) -> s32 #foreign libpq;
313 |
314 | /* backwards compatible version of PQcancel; not thread-safe */
315 | PQrequestCancel :: (conn: *PGconn) -> s32 #foreign libpq;
316 |
317 | /* Accessor functions for PGconn objects */
318 | PQdb :: (conn: *PGconn) -> *u8 #foreign libpq;
319 | PQuser :: (conn: *PGconn) -> *u8 #foreign libpq;
320 | PQpass :: (conn: *PGconn) -> *u8 #foreign libpq;
321 | PQhost :: (conn: *PGconn) -> *u8 #foreign libpq;
322 | PQhostaddr :: (conn: *PGconn) -> *u8 #foreign libpq;
323 | PQport :: (conn: *PGconn) -> *u8 #foreign libpq;
324 | PQtty :: (conn: *PGconn) -> *u8 #foreign libpq;
325 | PQoptions :: (conn: *PGconn) -> *u8 #foreign libpq;
326 | PQstatus :: (conn: *PGconn) -> ConnStatusType #foreign libpq;
327 | PQtransactionStatus :: (conn: *PGconn) -> PGTransactionStatusType #foreign libpq;
328 | PQparameterStatus :: (conn: *PGconn, paramName: *u8) -> *u8 #foreign libpq;
329 |
330 | PQprotocolVersion :: (conn: *PGconn) -> s32 #foreign libpq;
331 | PQserverVersion :: (conn: *PGconn) -> s32 #foreign libpq;
332 | PQerrorMessage :: (conn: *PGconn) -> *u8 #foreign libpq;
333 | PQsocket :: (conn: *PGconn) -> s32 #foreign libpq;
334 | PQbackendPID :: (conn: *PGconn) -> s32 #foreign libpq;
335 | PQpipelineStatus :: (conn: *PGconn) -> PGpipelineStatus #foreign libpq;
336 | PQconnectionNeedsPassword :: (conn: *PGconn) -> s32 #foreign libpq;
337 | PQconnectionUsedPassword :: (conn: *PGconn) -> s32 #foreign libpq;
338 | PQclientEncoding :: (conn: *PGconn) -> s32 #foreign libpq;
339 | PQsetClientEncoding :: (conn: *PGconn, encoding: *u8) -> s32 #foreign libpq;
340 |
341 | /* SSL information functions */
342 | PQsslInUse :: (conn: *PGconn) -> s32 #foreign libpq;
343 | PQsslStruct :: (conn: *PGconn, struct_name: *u8) -> *void #foreign libpq;
344 | PQsslAttribute :: (conn: *PGconn, attribute_name: *u8) -> *u8 #foreign libpq;
345 | PQsslAttributeNames :: (conn: *PGconn) -> **u8 #foreign libpq;
346 |
347 | /* Get the OpenSSL structure associated with a connection. Returns NULL for
348 | * unencrypted connections or if any other TLS library is in use. */
349 | PQgetssl :: (conn: *PGconn) -> *void #foreign libpq;
350 |
351 | /* Tell libpq whether it needs to initialize OpenSSL */
352 | PQinitSSL :: (do_init: s32) -> void #foreign libpq;
353 |
354 | /* More detailed way to tell libpq whether it needs to initialize OpenSSL */
355 | PQinitOpenSSL :: (do_ssl: s32, do_crypto: s32) -> void #foreign libpq;
356 |
357 | /* Return true if GSSAPI encryption is in use */
358 | PQgssEncInUse :: (conn: *PGconn) -> s32 #foreign libpq;
359 |
360 | /* Returns GSSAPI context if GSSAPI is in use */
361 | PQgetgssctx :: (conn: *PGconn) -> *void #foreign libpq;
362 |
363 | /* Set verbosity for PQerrorMessage and PQresultErrorMessage */
364 | PQsetErrorVerbosity :: (conn: *PGconn, verbosity: PGVerbosity) -> PGVerbosity #foreign libpq;
365 |
366 | /* Set CONTEXT visibility for PQerrorMessage and PQresultErrorMessage */
367 | PQsetErrorContextVisibility :: (conn: *PGconn, show_context: PGContextVisibility) -> PGContextVisibility #foreign libpq;
368 |
369 | /* Override default notice handling routines */
370 | PQsetNoticeReceiver :: (conn: *PGconn, proc: PQnoticeReceiver, arg: *void) -> PQnoticeReceiver #foreign libpq;
371 |
372 | PQsetNoticeProcessor :: (conn: *PGconn, proc: PQnoticeProcessor, arg: *void) -> PQnoticeProcessor #foreign libpq;
373 |
374 | /*
375 | * Used to set callback that prevents concurrent access to
376 | * non-thread safe functions that libpq needs.
377 | * The default implementation uses a libpq internal mutex.
378 | * Only required for multithreaded apps that use kerberos
379 | * both within their app and for postgresql connections.
380 | */
381 | pgthreadlock_t :: #type (acquire: s32) -> void #c_call;
382 |
383 | PQregisterThreadLock :: (newhandler: pgthreadlock_t) -> pgthreadlock_t #foreign libpq;
384 |
385 | /* === in fe-trace.c === */
386 | PQtrace :: (conn: *PGconn, debug_port: *FILE) -> void #foreign libpq;
387 | PQuntrace :: (conn: *PGconn) -> void #foreign libpq;
388 |
389 | PQsetTraceFlags :: (conn: *PGconn, flags: s32) -> void #foreign libpq;
390 |
391 | /* Simple synchronous query */
392 | PQexec :: (conn: *PGconn, query: *u8) -> *PGresult #foreign libpq;
393 | PQexecParams :: (conn: *PGconn, command: *u8, nParams: s32, paramTypes: *Oid, paramValues: **u8, paramLengths: *s32, paramFormats: *s32, resultFormat: s32) -> *PGresult #foreign libpq;
394 |
395 | PQprepare :: (conn: *PGconn, stmtName: *u8, query: *u8, nParams: s32, paramTypes: *Oid) -> *PGresult #foreign libpq;
396 |
397 | PQexecPrepared :: (conn: *PGconn, stmtName: *u8, nParams: s32, paramValues: **u8, paramLengths: *s32, paramFormats: *s32, resultFormat: s32) -> *PGresult #foreign libpq;
398 |
399 | PQsendQuery :: (conn: *PGconn, query: *u8) -> s32 #foreign libpq;
400 | PQsendQueryParams :: (conn: *PGconn, command: *u8, nParams: s32, paramTypes: *Oid, paramValues: **u8, paramLengths: *s32, paramFormats: *s32, resultFormat: s32) -> s32 #foreign libpq;
401 |
402 | PQsendPrepare :: (conn: *PGconn, stmtName: *u8, query: *u8, nParams: s32, paramTypes: *Oid) -> s32 #foreign libpq;
403 |
404 | PQsendQueryPrepared :: (conn: *PGconn, stmtName: *u8, nParams: s32, paramValues: **u8, paramLengths: *s32, paramFormats: *s32, resultFormat: s32) -> s32 #foreign libpq;
405 |
406 | PQsetSingleRowMode :: (conn: *PGconn) -> s32 #foreign libpq;
407 | PQgetResult :: (conn: *PGconn) -> *PGresult #foreign libpq;
408 |
409 | /* Routines for managing an asynchronous query */
410 | PQisBusy :: (conn: *PGconn) -> s32 #foreign libpq;
411 | PQconsumeInput :: (conn: *PGconn) -> s32 #foreign libpq;
412 |
413 | /* Routines for pipeline mode management */
414 | PQenterPipelineMode :: (conn: *PGconn) -> s32 #foreign libpq;
415 | PQexitPipelineMode :: (conn: *PGconn) -> s32 #foreign libpq;
416 | PQpipelineSync :: (conn: *PGconn) -> s32 #foreign libpq;
417 | PQsendFlushRequest :: (conn: *PGconn) -> s32 #foreign libpq;
418 |
419 | /* LISTEN/NOTIFY support */
420 | PQnotifies :: (conn: *PGconn) -> *PGnotify #foreign libpq;
421 |
422 | /* Routines for copy in/out */
423 | PQputCopyData :: (conn: *PGconn, buffer: *u8, nbytes: s32) -> s32 #foreign libpq;
424 | PQputCopyEnd :: (conn: *PGconn, errormsg: *u8) -> s32 #foreign libpq;
425 | PQgetCopyData :: (conn: *PGconn, buffer: **u8, async: s32) -> s32 #foreign libpq;
426 |
427 | /* Deprecated routines for copy in/out */
428 | PQgetline :: (conn: *PGconn, _string: *u8, length: s32) -> s32 #foreign libpq;
429 | PQputline :: (conn: *PGconn, _string: *u8) -> s32 #foreign libpq;
430 | PQgetlineAsync :: (conn: *PGconn, buffer: *u8, bufsize: s32) -> s32 #foreign libpq;
431 | PQputnbytes :: (conn: *PGconn, buffer: *u8, nbytes: s32) -> s32 #foreign libpq;
432 | PQendcopy :: (conn: *PGconn) -> s32 #foreign libpq;
433 |
434 | /* Set blocking/nonblocking connection to the backend */
435 | PQsetnonblocking :: (conn: *PGconn, arg: s32) -> s32 #foreign libpq;
436 | PQisnonblocking :: (conn: *PGconn) -> s32 #foreign libpq;
437 | PQisthreadsafe :: () -> s32 #foreign libpq;
438 | PQping :: (conninfo: *u8) -> PGPing #foreign libpq;
439 | PQpingParams :: (keywords: **u8, values: **u8, expand_dbname: s32) -> PGPing #foreign libpq;
440 |
441 | /* Force the write buffer to be written (or at least try) */
442 | PQflush :: (conn: *PGconn) -> s32 #foreign libpq;
443 |
444 | /*
445 | * "Fast path" interface --- not really recommended for application
446 | * use
447 | */
448 | PQfn :: (conn: *PGconn, fnid: s32, result_buf: *s32, result_len: *s32, result_is_int: s32, args: *PQArgBlock, nargs: s32) -> *PGresult #foreign libpq;
449 |
450 | /* Accessor functions for PGresult objects */
451 | PQresultStatus :: (res: *PGresult) -> ExecStatusType #foreign libpq;
452 | PQresStatus :: (status: ExecStatusType) -> *u8 #foreign libpq;
453 | PQresultErrorMessage :: (res: *PGresult) -> *u8 #foreign libpq;
454 | PQresultVerboseErrorMessage :: (res: *PGresult, verbosity: PGVerbosity, show_context: PGContextVisibility) -> *u8 #foreign libpq;
455 |
456 | PQresultErrorField :: (res: *PGresult, fieldcode: s32) -> *u8 #foreign libpq;
457 | PQntuples :: (res: *PGresult) -> s32 #foreign libpq;
458 | PQnfields :: (res: *PGresult) -> s32 #foreign libpq;
459 | PQbinaryTuples :: (res: *PGresult) -> s32 #foreign libpq;
460 | PQfname :: (res: *PGresult, field_num: s32) -> *u8 #foreign libpq;
461 | PQfnumber :: (res: *PGresult, field_name: *u8) -> s32 #foreign libpq;
462 | PQftable :: (res: *PGresult, field_num: s32) -> Oid #foreign libpq;
463 | PQftablecol :: (res: *PGresult, field_num: s32) -> s32 #foreign libpq;
464 | PQfformat :: (res: *PGresult, field_num: s32) -> s32 #foreign libpq;
465 | PQftype :: (res: *PGresult, field_num: s32) -> Oid #foreign libpq;
466 | PQfsize :: (res: *PGresult, field_num: s32) -> s32 #foreign libpq;
467 | PQfmod :: (res: *PGresult, field_num: s32) -> s32 #foreign libpq;
468 | PQcmdStatus :: (res: *PGresult) -> *u8 #foreign libpq;
469 | PQoidStatus :: (res: *PGresult) -> *u8 #foreign libpq;
470 | PQoidValue :: (res: *PGresult) -> Oid #foreign libpq;
471 | PQcmdTuples :: (res: *PGresult) -> *u8 #foreign libpq;
472 | PQgetvalue :: (res: *PGresult, tup_num: s32, field_num: s32) -> *u8 #foreign libpq;
473 | PQgetlength :: (res: *PGresult, tup_num: s32, field_num: s32) -> s32 #foreign libpq;
474 | PQgetisnull :: (res: *PGresult, tup_num: s32, field_num: s32) -> s32 #foreign libpq;
475 | PQnparams :: (res: *PGresult) -> s32 #foreign libpq;
476 | PQparamtype :: (res: *PGresult, param_num: s32) -> Oid #foreign libpq;
477 |
478 | /* Describe prepared statements and portals */
479 | PQdescribePrepared :: (conn: *PGconn, stmt: *u8) -> *PGresult #foreign libpq;
480 | PQdescribePortal :: (conn: *PGconn, portal: *u8) -> *PGresult #foreign libpq;
481 | PQsendDescribePrepared :: (conn: *PGconn, stmt: *u8) -> s32 #foreign libpq;
482 | PQsendDescribePortal :: (conn: *PGconn, portal: *u8) -> s32 #foreign libpq;
483 |
484 | /* Delete a PGresult */
485 | PQclear :: (res: *PGresult) -> void #foreign libpq;
486 |
487 | /* For freeing other alloc'd results, such as PGnotify structs */
488 | PQfreemem :: (ptr: *void) -> void #foreign libpq;
489 |
490 | /* Create and manipulate PGresults */
491 | PQmakeEmptyPGresult :: (conn: *PGconn, status: ExecStatusType) -> *PGresult #foreign libpq;
492 | PQcopyResult :: (src: *PGresult, flags: s32) -> *PGresult #foreign libpq;
493 | PQsetResultAttrs :: (res: *PGresult, numAttributes: s32, attDescs: *PGresAttDesc) -> s32 #foreign libpq;
494 | PQresultAlloc :: (res: *PGresult, nBytes: size_t) -> *void #foreign libpq;
495 | PQresultMemorySize :: (res: *PGresult) -> size_t #foreign libpq;
496 | PQsetvalue :: (res: *PGresult, tup_num: s32, field_num: s32, value: *u8, len: s32) -> s32 #foreign libpq;
497 |
498 | /* Quoting strings before inclusion in queries. */
499 | PQescapeStringConn :: (conn: *PGconn, to: *u8, from: *u8, length: size_t, error: *s32) -> size_t #foreign libpq;
500 |
501 | PQescapeLiteral :: (conn: *PGconn, str: *u8, len: size_t) -> *u8 #foreign libpq;
502 | PQescapeIdentifier :: (conn: *PGconn, str: *u8, len: size_t) -> *u8 #foreign libpq;
503 | PQescapeByteaConn :: (conn: *PGconn, from: *u8, from_length: size_t, to_length: *size_t) -> *u8 #foreign libpq;
504 |
505 | PQunescapeBytea :: (strtext: *u8, retbuflen: *size_t) -> *u8 #foreign libpq;
506 |
507 | /* These forms are deprecated! */
508 | PQescapeString :: (to: *u8, from: *u8, length: size_t) -> size_t #foreign libpq;
509 | PQescapeBytea :: (from: *u8, from_length: size_t, to_length: *size_t) -> *u8 #foreign libpq;
510 |
511 | /* === in fe-print.c === */
512 | PQprint :: (fout: *FILE, res: *PGresult, ps: *PQprintOpt) -> void #foreign libpq;
513 |
514 | /*
515 | * really old printing routines
516 | */
517 | PQdisplayTuples :: (res: *PGresult, fp: *FILE, fillAlign: s32, fieldSep: *u8, printHeader: s32, quiet: s32) -> void #foreign libpq;
518 |
519 | PQprintTuples :: (res: *PGresult, fout: *FILE, PrintAttNames: s32, TerseOutput: s32, colWidth: s32) -> void #foreign libpq;
520 |
521 | /* Large-object access routines */
522 | lo_open :: (conn: *PGconn, lobjId: Oid, mode: s32) -> s32 #foreign libpq;
523 | lo_close :: (conn: *PGconn, fd: s32) -> s32 #foreign libpq;
524 | lo_read :: (conn: *PGconn, fd: s32, buf: *u8, len: size_t) -> s32 #foreign libpq;
525 | lo_write :: (conn: *PGconn, fd: s32, buf: *u8, len: size_t) -> s32 #foreign libpq;
526 | lo_lseek :: (conn: *PGconn, fd: s32, offset: s32, whence: s32) -> s32 #foreign libpq;
527 | lo_lseek64 :: (conn: *PGconn, fd: s32, offset: pg_int64, whence: s32) -> pg_int64 #foreign libpq;
528 | lo_creat :: (conn: *PGconn, mode: s32) -> Oid #foreign libpq;
529 | lo_create :: (conn: *PGconn, lobjId: Oid) -> Oid #foreign libpq;
530 | lo_tell :: (conn: *PGconn, fd: s32) -> s32 #foreign libpq;
531 | lo_tell64 :: (conn: *PGconn, fd: s32) -> pg_int64 #foreign libpq;
532 | lo_truncate :: (conn: *PGconn, fd: s32, len: size_t) -> s32 #foreign libpq;
533 | lo_truncate64 :: (conn: *PGconn, fd: s32, len: pg_int64) -> s32 #foreign libpq;
534 | lo_unlink :: (conn: *PGconn, lobjId: Oid) -> s32 #foreign libpq;
535 | lo_import :: (conn: *PGconn, filename: *u8) -> Oid #foreign libpq;
536 | lo_import_with_oid :: (conn: *PGconn, filename: *u8, lobjId: Oid) -> Oid #foreign libpq;
537 | lo_export :: (conn: *PGconn, lobjId: Oid, filename: *u8) -> s32 #foreign libpq;
538 |
539 | /* Get the version of the libpq library in use */
540 | PQlibVersion :: () -> s32 #foreign libpq;
541 |
542 | /* Determine length of multibyte encoded char at *s */
543 | PQmblen :: (s: *u8, encoding: s32) -> s32 #foreign libpq;
544 |
545 | /* Same, but not more than the distance to the end of string s */
546 | PQmblenBounded :: (s: *u8, encoding: s32) -> s32 #foreign libpq;
547 |
548 | /* Determine display length of multibyte encoded char at *s */
549 | PQdsplen :: (s: *u8, encoding: s32) -> s32 #foreign libpq;
550 |
551 | /* Get encoding id from environment variable PGCLIENTENCODING */
552 | PQenv2encoding :: () -> s32 #foreign libpq;
553 |
554 | /* === in fe-auth.c === */
555 | PQencryptPassword :: (passwd: *u8, user: *u8) -> *u8 #foreign libpq;
556 | PQencryptPasswordConn :: (conn: *PGconn, passwd: *u8, user: *u8, algorithm: *u8) -> *u8 #foreign libpq;
557 |
558 | /* === in encnames.c === */
559 | pg_char_to_encoding :: (name: *u8) -> s32 #foreign libpq;
560 | pg_encoding_to_char :: (encoding: s32) -> *u8 #foreign libpq;
561 | pg_valid_server_encoding_id :: (encoding: s32) -> s32 #foreign libpq;
562 |
563 | /* Support for overriding sslpassword handling with a callback */
564 | PQsslKeyPassHook_OpenSSL_type :: #type (buf: *u8, size: s32, conn: *PGconn) -> s32 #c_call;
565 | PQgetSSLKeyPassHook_OpenSSL :: () -> PQsslKeyPassHook_OpenSSL_type #foreign libpq;
566 | PQsetSSLKeyPassHook_OpenSSL :: (hook: PQsslKeyPassHook_OpenSSL_type) -> void #foreign libpq;
567 | PQdefaultSSLKeyPassHook_OpenSSL :: (buf: *u8, size: s32, conn: *PGconn) -> s32 #foreign libpq;
568 |
569 | #scope_file
570 |
571 |
572 | libpq :: #library,no_dll "libpq";
573 |
574 |
--------------------------------------------------------------------------------
/byte_order.jai:
--------------------------------------------------------------------------------
1 | IS_BIG_ENDIAN :: #run big_endian();
2 |
3 | big_endian :: () -> bool {
4 | x: s16 = 1;
5 | b := cast(*u8) *x;
6 |
7 | if b[0] return false;
8 | return true;
9 | }
10 |
11 | ntoh :: inline (value: $T) -> T {
12 | #if IS_BIG_ENDIAN {
13 | return value;
14 | } else {
15 | return byte_swap(value);
16 | }
17 | }
18 |
19 | hton :: ntoh;
20 |
21 | #scope_file
22 |
23 | #import "Basic";
24 | #import "Compiler";
25 | #import "Bit_Operations";
26 |
27 |
--------------------------------------------------------------------------------
/examples/example.jai:
--------------------------------------------------------------------------------
1 | main :: () {
2 | args := get_command_line_arguments();
3 | if args.count != 2 {
4 | log_error("Usage: % ", args[0]);
5 | exit(1);
6 | }
7 |
8 | db_url := args[1];
9 |
10 | conn, success := connect(db_url);
11 | defer disconnect(conn);
12 | if !success exit(1);
13 |
14 | // Let’s just test deserializing all the supported column types:
15 | log("Test 1");
16 | QUERY :: #string END
17 | SELECT
18 | 1::int2, 2::int4, 3::int8,
19 | 2.5::float8, 3.5::float8 as float8_to_4, 4.5::float4, 5.5::float4 as float4_to_8, 6.5::numeric(8, 2),
20 | true "boolean_true",
21 | false "boolean_false",
22 | 'a'::char(3) "char", 'b'::bpchar "bpchar", 'c'::varchar, 'd'::name, 'e'::text,
23 | '2023-05-15'::timestamp, '2023-05-14 UTC'::timestamptz,
24 | '2023-05-15T14:30:00'::timestamp time_wo_tz, '2023-05-15T14:30:00 Europe/Vienna'::timestamptz time_with_tz, '2023-05-15T14:30:00 UTC'::timestamptz time_utc,
25 | '2023-05-15T14:30:00'::timestamp timestamp_apollo, -- Test writing timestamp into Apollo_Time
26 | '2023-10-24'::date real_date,
27 | -- '2023-10-24'::date real_date_apollo, -- Test writing date into Apollo_Time
28 | 'Hello, Sailor!'::bytea sailor_bytes, -- Test [] case
29 | 'Hello, Sailor!'::bytea sailor_bytes_dynamic, -- Test [..] case
30 | 'Hello, Sailor!'::bytea sailor_bytes_fixed, -- Test [n] case
31 | '01951a87-c21c-726c-ba20-e0da37db52cf'::UUID -- UUID (which is a [16]u8) test case
32 | END
33 |
34 | results: [] Test;
35 | results, success = execute(conn, Test, QUERY);
36 | assert(success);
37 | log("Results: %", results);
38 | assert(results.count == 1);
39 |
40 | result := results[0];
41 | assert(result.int2 == 1);
42 | assert(result.int4 == 2);
43 | assert(result.int8 == 3);
44 | assert(result.float8 == 2.5);
45 | assert(result.float8_to_4 == 3.5);
46 | assert(result.float4 == 4.5);
47 | assert(result.float4_to_8 == 5.5);
48 | assert(result.numeric == "6.50");
49 | assert(result.boolean_true == true);
50 | assert(result.boolean_false == false);
51 | assert(result.char == "a ");
52 | assert(result.bpchar == "b");
53 | assert(result.varchar == "c");
54 | assert(result.name == "d");
55 | assert(result.text == "e");
56 | assert(result.timestamp == "2023-05-15T00:00:00.000Z");
57 | assert(result.timestamptz == 1684022400000000);
58 | assert(calendar_to_iso_string(to_calendar(result.timestamp_apollo)) == "2023-05-15T14:30:00.000Z");
59 | assert(result.real_date == 8697); // Days since 2000-01-01
60 | // assert(calendar_to_iso_string(to_calendar(result.real_date_apollo)) == "2023-10-24T00:00:00.000Z");
61 | assert(cast(string) result.sailor_bytes == "Hello, Sailor!");
62 | assert(cast(string) result.sailor_bytes_dynamic == "Hello, Sailor!");
63 | assert(cast(string) result.sailor_bytes_fixed == "Hello, Sailor!");
64 | // These bytes are the UUIDv7: 01951a87-c21c-726c-ba20-e0da37db52cf
65 | assert(uuid_equal(result.uuid, u8.[1, 149, 26, 135, 194, 28, 114, 108, 186, 32, 224, 218, 55, 219, 82, 207]));
66 | log("% vs % vs %", result.time_wo_tz, result.time_with_tz, result.time_utc);
67 |
68 | // Let’s try a serializing-deserializing roundtrip
69 | log("Test 2");
70 | QUERY2 :: #string END
71 | SELECT
72 | $1::int2,
73 | $2::float8, $3::float4,
74 | $4 "boolean_true",
75 | $5 "boolean_false",
76 | $6::varchar,
77 | $7::text
78 | END
79 |
80 | v1: int = 256;
81 | v2: float64 = 1.5;
82 | v3: float = 2.5;
83 | v4 := true;
84 | v5 := false;
85 | v6: string;
86 | v7 := "Something";
87 | results, success = execute(conn, Test, QUERY2, v1, v2, v3, v4, v5, v6, v7);
88 | assert(success);
89 | log("Results: %", results);
90 | assert(results.count == 1);
91 |
92 | result = results[0];
93 | assert(result.int2 == 256);
94 | assert(result.float8 == 1.5);
95 | assert(result.float4 == 2.5);
96 | assert(result.boolean_true == true);
97 | assert(result.boolean_false == false);
98 | assert(result.varchar == "");
99 | assert(result.text == "Something");
100 |
101 | log("ALL OK");
102 | }
103 |
104 | UUID :: [16]u8;
105 |
106 | // UUIDs are just two s64 values, so equality is simply comparing each
107 | // of the two s64 values to each other from u1 and u2
108 | uuid_equal :: (u1: UUID, u2: UUID) -> bool {
109 | a, b := u1.([2]s64,force), u2.([2]s64,force);
110 | return a[0] == b[0] && a[1] == b[1];
111 | }
112 |
113 | uuid_to_string :: (uuid: UUID) -> string {
114 |
115 | push_context {
116 |
117 | context.print_style.default_format_int.base = 16;
118 | context.print_style.default_format_int.minimum_digits = 2;
119 |
120 | formatted := sprint("%0%0%0%0-%0%0-%0%0-%0%0-%0%0%0%0%0%0",
121 | uuid[0], uuid[1], uuid[2], uuid[3],
122 | uuid[4], uuid[5],
123 | uuid[6], uuid[7],
124 | uuid[8], uuid[9],
125 | uuid[10], uuid[11],
126 | uuid[12], uuid[13], uuid[14], uuid[15],
127 | );
128 |
129 | return formatted;
130 | }
131 | }
132 |
133 | Test :: struct {
134 | int2: int;
135 | int4: int;
136 | int8: s64;
137 | float8: float64;
138 | float8_to_4: float32;
139 | float4: float32;
140 | float4_to_8: float64;
141 | numeric: string;
142 | boolean_true: bool;
143 | boolean_false: bool;
144 | char: string;
145 | bpchar: string;
146 | varchar: string;
147 | name: string;
148 | text: string;
149 | timestamp: string;
150 | timestamptz: s64;
151 | timestamp_apollo: Apollo_Time;
152 | time_wo_tz: s64;
153 | time_with_tz: s64;
154 | time_utc: s64;
155 | real_date: s64;
156 | // real_date_apollo: Apollo_Time; // Only works with SUPPORT_DATE_AS_STRINGS
157 | sailor_bytes: [] u8;
158 | sailor_bytes_dynamic: [..] u8;
159 | sailor_bytes_fixed: [14] u8;
160 | uuid: UUID;
161 | }
162 |
163 | #import,file "../module.jai";
164 | #import "Basic";
165 |
--------------------------------------------------------------------------------
/examples/first.jai:
--------------------------------------------------------------------------------
1 | #run {
2 | set_build_options_dc(.{do_output = false});
3 |
4 | w := compiler_create_workspace();
5 | options := get_build_options();
6 | options.output_type = .EXECUTABLE;
7 | options.output_executable_name = "example";
8 | if OS == .MACOS {
9 | HOMEBREW_LIB_PATH :: "/usr/local/homebrew/lib"; // This is where my x64 libraries live. Adjust to fit your needs.
10 | options.additional_linker_arguments = .["-L", HOMEBREW_LIB_PATH];
11 | compiler_add_library_search_directory(HOMEBREW_LIB_PATH);
12 | }
13 |
14 | set_build_options(options, w);
15 |
16 | add_build_file("example.jai", w);
17 | }
18 |
19 | #import "Compiler";
20 |
21 |
--------------------------------------------------------------------------------
/first.jai:
--------------------------------------------------------------------------------
1 | #import "Compiler";
2 |
3 | #run build();
4 |
5 | // Just try to compile the module source without producing an executable to check for obvious errors.
6 | build :: () {
7 | set_build_options_dc(.{do_output = false});
8 |
9 | w := compiler_create_workspace();
10 | build_options := get_build_options(w);
11 | build_options.output_type = .NO_OUTPUT;
12 | set_build_options(build_options, w);
13 |
14 | add_build_file("module.jai", w);
15 | }
16 |
17 |
--------------------------------------------------------------------------------
/generate.jai:
--------------------------------------------------------------------------------
1 | AT_COMPILE_TIME :: true;
2 |
3 | WINDOWS_PG_DIR :: "C:/Program Files/PostgreSQL/15";
4 | IMGUI_PATH :: "src/imgui-1.85";
5 |
6 | DECLARATIONS_TO_OMIT :: string.[
7 | ];
8 |
9 | #if AT_COMPILE_TIME {
10 | #run {
11 | set_build_options_dc(.{do_output=false});
12 | if !generate_bindings() {
13 | compiler_set_workspace_status(.FAILED);
14 | }
15 | }
16 | } else {
17 | #import "System";
18 |
19 | main :: () {
20 | set_working_directory(path_strip_filename(get_path_of_running_executable()));
21 | if !generate_bindings() {
22 | exit(1);
23 | }
24 | }
25 | }
26 |
27 | generate_bindings :: () -> bool {
28 | output_filename: string;
29 | opts: Generate_Bindings_Options;
30 | {
31 | using opts;
32 |
33 | make_directory_if_it_does_not_exist("bindings");
34 |
35 | #if OS == .WINDOWS {
36 | output_filename = "bindings/windows.jai";
37 | array_add(*include_paths, tprint("%/%", WINDOWS_PG_DIR, "include"));
38 | array_add(*libpaths, tprint("%/%", WINDOWS_PG_DIR, "bin"));
39 | array_add(*libpaths, tprint("%/%", WINDOWS_PG_DIR, "lib"));
40 | generate_library_declarations = false;
41 | footer = FOOTER_WIN;
42 | } else #if OS == .LINUX {
43 | output_filename = "bindings/unix.jai";
44 | array_add(*include_paths, "/usr/include/postgresql");
45 | generate_library_declarations = false;
46 | footer = FOOTER_UNIX;
47 | } else #if OS == .MACOS {
48 | // @ToDo: Determine dynamically? Customize?
49 | array_add(*system_include_paths, "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include");
50 | array_add(*include_paths, "/usr/local/homebrew/opt/libpq/include");
51 | array_add(*libpaths, "/usr/local/homebrew/opt/libpq/lib");
52 | output_filename = "bindings/unix.jai";
53 | generate_library_declarations = false;
54 | footer = FOOTER_UNIX;
55 | } else {
56 | assert(false);
57 | }
58 |
59 | array_add(*libnames, "libpq");
60 | array_add(*system_include_paths, GENERATOR_DEFAULT_SYSTEM_INCLUDE_PATH);
61 | array_add(*source_files, "libpq-fe.h");
62 | array_add(*extra_clang_arguments, "-x", "c");
63 | auto_detect_enum_prefixes = false;
64 | generate_compile_time_struct_checks = false;
65 | strip_flags |= .INLINED_FUNCTIONS; // "inline" functions are not present in the static library.
66 |
67 | // visitor = imgui_visitor;
68 | }
69 |
70 | return generate_bindings(opts, output_filename);
71 | }
72 |
73 | //imgui_visitor :: (decl: *Declaration, parent_decl: *Declaration) -> Declaration_Visit_Result {
74 | // // ImGui has "typedef ints" for each enum. but we want to just use the enums directly,
75 | // // so we get nice type safety, and .unary dots, etc.
76 | // //
77 | // // Look for declarations inside functions (i.e., arguments) or structs (i.e., fields)
78 | // get_associated_enum_name :: (name: string) -> string {
79 | // if name.count > 1 && name[name.count - 1] != #char "_" {
80 | // return tprint("%_", name);
81 | // }
82 | // return name;
83 | // }
84 |
85 | // if !parent_decl {
86 | // if array_find(DECLARATIONS_TO_OMIT, decl.name) {
87 | // decl.decl_flags |= .OMIT_FROM_OUTPUT;
88 | // return .STOP;
89 | // }
90 | // }
91 |
92 | // // Look for function arguments with "typedef" types.
93 | // if parent_decl &&
94 | // (parent_decl.kind == .FUNCTION || parent_decl.kind == .STRUCT) &&
95 | // decl.kind == .DECLARATION && decl.type.type_of_typedef != null
96 | // {
97 | // old_name := decl.type.type_of_typedef.name;
98 | // if !old_name return .RECURSE;
99 | // new_name := get_associated_enum_name(old_name);
100 |
101 | // //print("% -> %\n", old_name, new_name);
102 |
103 | // for context.generator.global_scope.members {
104 | // if it.kind != .ENUM || it.name != new_name continue;
105 |
106 | // en := cast(*Enum)it;
107 |
108 | // // Don't output the integer typedef
109 | // decl.type.type_of_typedef.decl_flags |= .OMIT_FROM_OUTPUT;
110 |
111 | // // Make sure the enums like "FocusFlags_" get outputted as "FocusFlags"
112 | // if en.output_name.count > 2 && en.output_name[en.output_name.count - 1] == #char "_" {
113 | // en.output_name.count -= 1;
114 | // }
115 |
116 | // // swap in the enum for the typedef
117 | // decl.type.type_of_typedef = null;
118 | // decl.type.type_of_enum = en;
119 |
120 | // // we'll also need to output a cast, like "cast(EnumType)5" for the default argument
121 | // decl.decl_flags |= .NEEDS_DEFAULT_ARGUMENT_CAST;
122 |
123 | // break;
124 | // }
125 | // }
126 |
127 | // if decl.kind == .ENUM {
128 | // // ImGui has lots of enums that should be enum flags. Luckily, they all end in "…Flags_".
129 | // if ends_with(decl.name, "Flags_") {
130 | // en := cast(*Enum)decl;
131 | // en.flags |= .IS_ENUM_FLAGS;
132 | // en.flags |= .VALUES_IN_HEX;
133 | // }
134 | // }
135 |
136 | // if decl.kind == .FUNCTION {
137 | // func := cast(*Function)decl;
138 | // type := func.type.type_of_function;
139 | // // ImGui also has functions with pairs of arguments like "text_begin" and "text_end"
140 | // // for the pointers to the beginning and end of a string. We'll generate wrappers for
141 | // // those function which take a jai string.
142 | // //
143 | // // Here we collect pointers to "_begin" arguments, and use them in
144 | // // get_func_args_for_printing below.
145 | // for type.arguments {
146 | // if it_index == 0 || !ends_with(it.name, "_end") continue;
147 |
148 | // name_part := slice(it.name, 0, it.name.count - "_end".count);
149 | // if !name_part continue;
150 |
151 | // // the first arg may appear as "text_begin" or just "text"
152 | // name_part_with_begin := tprint("%_begin", name_part);
153 | // prev_arg := type.arguments[it_index - 1];
154 | // if prev_arg.name == name_part || prev_arg.name == name_part_with_begin {
155 | // array_add(*begin_end_string_args, prev_arg);
156 | // func.decl_flags |= .NEEDS_ARGUMENT_WRAPPER;
157 | // }
158 | // }
159 | // }
160 |
161 | // return .RECURSE;
162 | //}
163 |
164 | #import "Basic";
165 | #import "Bindings_Generator";
166 | #import "Compiler";
167 | #import "File";
168 | #import "String";
169 |
170 | FOOTER_UNIX :: #string END
171 |
172 | libpq :: #system_library "libpq";
173 |
174 | END
175 |
176 | FOOTER_WIN :: #string END
177 |
178 | libpq :: #library,no_dll "libpq";
179 |
180 | END
--------------------------------------------------------------------------------
/generate_types.jai:
--------------------------------------------------------------------------------
1 | // Adapted from node-pg-types (https://github.com/brianc/node-pg-types/blob/master/lib/binaryParsers.js)
2 | // From their doc:
3 | // Following query was used to generate this file:
4 | // SELECT json_object_agg(UPPER(PT.typname), PT.oid::int4 ORDER BY pt.oid)
5 | // FROM pg_type PT
6 | // WHERE typnamespace = (SELECT pgn.oid FROM pg_namespace pgn WHERE nspname = 'pg_catalog') -- Take only builting Postgres types with stable OID (extension types are not guaranted to be stable)
7 | // AND typtype = 'b' -- Only basic types
8 | // AND typelem = 0 -- Ignore aliases
9 | // AND typisdefined -- Ignore undefined types
10 |
11 | #run {
12 | #import "Compiler";
13 | set_build_options_dc(.{do_output = false});
14 | args := get_build_options().compile_time_command_line;
15 | if args.count != 1 {
16 | log_error("Usage: jai % - ", #file);
17 | exit(1);
18 | }
19 |
20 | db_url := args[0];
21 | success := generate_types(db_url);
22 | if !success compiler_report("Error while generating types");
23 | }
24 |
25 | generate_types :: (db_url: string) -> bool {
26 | conn, success := connect(db_url);
27 | defer disconnect(conn);
28 | if !success {
29 | log_error("Could not connect to \"%\"", db_url);
30 | return false;
31 | }
32 |
33 | TYPE_QUERY :: #string END
34 | SELECT UPPER(pt.typname) typname, pt.oid, pt.typarray
35 | FROM pg_type pt
36 | WHERE typnamespace = (SELECT pgn.oid FROM pg_namespace pgn WHERE nspname = 'pg_catalog') -- Take only builting Postgres types with stable OID (extension types are not guaranted to be stable)
37 | AND typtype = 'b' -- Only basic types
38 | AND typname NOT LIKE '\_%' -- ignore names starting with "_", which seem to be 1:1 aliases for the ones without "_"
39 | AND typisdefined -- Ignore undefined types
40 | ORDER BY pt.oid
41 | END
42 |
43 | type_list:, success = execute(conn, Sql_Type, TYPE_QUERY);
44 | if !success {
45 | log_error("Could not fetch types");
46 | return false;
47 | }
48 |
49 | max_len := 0;
50 | for type_list {
51 | if it.typname.count > max_len max_len = it.typname.count;
52 | }
53 |
54 | builder: String_Builder;
55 | print_to_builder(*builder, "// Generated via \"%\"\n\n", path_filename(#file));
56 | append(*builder, "Pq_Type :: enum Oid {\n");
57 | for type_list {
58 | print_to_builder(*builder, " %", it.typname);
59 | for it.typname.count..max_len {
60 | append(*builder, " ");
61 | }
62 | print_to_builder(*builder, ":: %;\n", it.oid);
63 | }
64 | append(*builder, "}\n\n");
65 | append(*builder, "Pq_Array_Type :: enum Oid {\n");
66 | for type_list {
67 | if it.typarray == 0 continue;
68 | print_to_builder(*builder, " %", it.typname);
69 | for it.typname.count..max_len {
70 | append(*builder, " ");
71 | }
72 | print_to_builder(*builder, ":: %;\n", it.typarray);
73 | }
74 | append(*builder, "}\n");
75 |
76 | FILENAME :: "pq_types.jai";
77 | success = write_entire_file(FILENAME, *builder);
78 | if !success {
79 | log_error("Could not write to file \"%\".", FILENAME);
80 | return false;
81 | }
82 |
83 | return true;
84 | }
85 |
86 | Sql_Type :: struct {
87 | typname: string;
88 | oid: int;
89 | typarray: int;
90 | }
91 |
92 | #import "Basic";
93 | #import "File";
94 | #import "String";
95 | #import,file "module.jai";
96 |
--------------------------------------------------------------------------------
/module.jai:
--------------------------------------------------------------------------------
1 | #module_parameters ()(SUPPORT_DATE_AS_STRINGS := false); // SUPPORT_DATE requires https://github.com/rluba/jai-date, at least for now.
2 |
3 | connect :: (conn_str: string) -> *PGconn, success: bool {
4 | conn_c_str := to_c_string(conn_str);
5 | defer free(conn_c_str);
6 | conn := PQconnectStart(conn_c_str);
7 | if PQstatus(conn) == ConnStatusType.CONNECTION_BAD {
8 | log_error("Couldn’t start connecting\n");
9 | return conn, false;
10 | }
11 |
12 | result := PQconnectPoll(conn);
13 | while result != PostgresPollingStatusType.PGRES_POLLING_OK && result != PostgresPollingStatusType.PGRES_POLLING_FAILED {
14 | newResult := PQconnectPoll(conn);
15 | // if (result != newResult) {
16 | // print("Connecting… %\n", result);
17 | // }
18 | result = newResult;
19 | }
20 |
21 | if result == PostgresPollingStatusType.PGRES_POLLING_FAILED {
22 | log_error("Couldn’t connect: %\n", PQstatus(conn));
23 | return conn, false;
24 | }
25 | return conn, true;
26 | }
27 |
28 | disconnect :: (conn: *PGconn) {
29 | if conn {
30 | PQfinish(conn);
31 | }
32 | }
33 |
34 | // Execute a statement and parse the result
35 | execute :: (conn: *PGconn, $T: Type, command: string, args: .. Any, $ignore_unknown := false) -> [] T, success: bool {
36 | success := send_query(conn, command, ..args);
37 | if !success return .[], false;
38 |
39 | query_res := get_last_query_result(conn);
40 | defer PQclear(query_res);
41 |
42 | has_results: bool;
43 | results: [] T;
44 | has_results, success = check_query_result(query_res);
45 | if !success return results, false;
46 |
47 | if has_results {
48 | results, success = get_results(query_res, T, ignore_unknown);
49 | }
50 | return results, success;
51 | }
52 |
53 | // Execute a statement without parsing the result (eg. for DELETE et al)
54 | execute :: (conn: *PGconn, command: string, args: .. Any) -> success: bool, update_rows: int {
55 | success := send_query(conn, command, ..args);
56 | if !success false;
57 |
58 | query_res := get_last_query_result(conn);
59 | defer PQclear(query_res);
60 |
61 | has_results: bool;
62 | update_rows: int;
63 | has_results, success, update_rows = check_query_result(query_res);
64 |
65 | return success, update_rows;
66 | }
67 |
68 | send_query :: (conn: *PGconn, command: string, args: .. Any) -> success: bool {
69 | pool: Pool;
70 | set_allocators(*pool);
71 | defer release(*pool);
72 |
73 | push_allocator(pool_allocator_proc, *pool);
74 |
75 | param_types := NewArray(args.count, Oid, initialized = false);
76 | param_values := NewArray(args.count, *u8, initialized = false);
77 | param_lengths := NewArray(args.count, s32, initialized = false);
78 | param_formats := NewArray(args.count, s32, initialized = false);
79 |
80 | for arg, index: args {
81 | if arg.type.type == {
82 | case .INTEGER;
83 | info := cast(*Type_Info_Integer) arg.type;
84 | // @ToDo: implement unsigned ints
85 | assert(info.signed, "Unsigned not yet supported");
86 | be_value := cast(*u8) alloc(info.runtime_size);
87 | if info.runtime_size == {
88 | case 2;
89 | param_types[index] = cast(Oid) Pq_Type.INT2;
90 | be_val := hton(<<(cast(*s16) arg.value_pointer));
91 | memcpy(be_value, *be_val, info.runtime_size);
92 | case 4;
93 | param_types[index] = cast(Oid) Pq_Type.INT4;
94 | be_val := hton(<<(cast(*s32) arg.value_pointer));
95 | memcpy(be_value, *be_val, info.runtime_size);
96 | case 8;
97 | param_types[index] = cast(Oid) Pq_Type.INT8;
98 | be_val := hton(<<(cast(*s64) arg.value_pointer));
99 | memcpy(be_value, *be_val, info.runtime_size);
100 | case;
101 | assert(false);
102 | }
103 | param_values[index] = be_value;
104 | param_lengths[index] = cast(s32) info.runtime_size;
105 | param_formats[index] = 1;
106 |
107 | case .FLOAT;
108 | be_value := cast(*u8) alloc(arg.type.runtime_size);
109 | if arg.type.runtime_size == 4 {
110 | param_types[index] = cast(Oid) Pq_Type.FLOAT4;
111 | be_val := hton(<<(cast(*float) arg.value_pointer));
112 | memcpy(be_value, *be_val, arg.type.runtime_size);
113 | }
114 | if arg.type.runtime_size == 8 {
115 | param_types[index] = cast(Oid) Pq_Type.FLOAT8;
116 | be_val := hton(<<(cast(*float64) arg.value_pointer));
117 | memcpy(be_value, *be_val, arg.type.runtime_size);
118 | }
119 | param_values[index] = be_value;
120 | param_lengths[index] = cast(s32) arg.type.runtime_size;
121 | param_formats[index] = 1;
122 |
123 | case .STRING;
124 | str := cast(*string) arg.value_pointer;
125 | param_types[index] = cast(Oid) Pq_Type.TEXT;
126 | param_values[index] = ifx str.data else ""; // This handles empty strings. Sending no data means null string
127 | param_lengths[index] = cast(s32) str.count;
128 | param_formats[index] = 1;
129 |
130 | case .BOOL;
131 | val := cast(*bool) arg.value_pointer;
132 | param_types[index] = cast(Oid) Pq_Type.BOOL;
133 | param_values[index] = ifx val.* then *BOOL_TRUE_VALUE else *BOOL_FALSE_VALUE;
134 | param_lengths[index] = 1;
135 | param_formats[index] = 1;
136 |
137 | // case .ARRAY;
138 | // @ToDo: Implement binary format according to array_recv format.
139 | // See https://doxygen.postgresql.org/arrayfuncs_8c.html#a315b67e6e01e8f283326b5a6b27e07c9
140 | //
141 | // Format seems to be:
142 | // 4 byte number of dimensions
143 | // 4 byte flags (seems unused, but must be 0 or 1)
144 | // 4 byte element type (sizeof OID, actually…)
145 | // Then for each dimension:
146 | // 4 byte number of elements
147 | // 4 byte lower bound
148 | // Then for each element:
149 | // 4 byte length
150 | // n byte encoded element
151 | //
152 | // But given the overhead, maybe it would be smarter to just serialize arrays as strings?
153 | // Eg. an array of two ints would be 28 bytes + 2 * int size.
154 | // The string equivalent would be 3 bytes + string-size of the integers
155 |
156 | // ToDo: Struct as Jsonb?
157 |
158 | case;
159 | // @ToDo: Implement
160 | log_error("Unsupported param type: %", arg.type.type);
161 | return false;
162 | }
163 | }
164 |
165 | c_command := to_c_string(command);
166 |
167 | result: int;
168 | result = PQsendQueryParams(conn, c_command, cast(s32) args.count, param_types.data, param_values.data, param_lengths.data, param_formats.data, 1);
169 | if result == 0 {
170 | error_message: string;
171 | error_message.data = PQerrorMessage(conn);
172 | error_message.count = c_style_strlen(error_message.data);
173 | log_error("Could not send query: %", error_message);
174 | return false;
175 | }
176 |
177 | return true;
178 | }
179 |
180 |
181 | get_last_query_result :: (conn: *PGconn) -> *PGresult {
182 | query_result: *PGresult;
183 | while true {
184 | latest_result := PQgetResult(conn);
185 | if latest_result == null break;
186 | if query_result PQclear(query_result);
187 | query_result = latest_result;
188 | }
189 |
190 | return query_result;
191 | }
192 |
193 | check_query_result :: (query_res: *PGresult) -> has_results: bool, success: bool, updated_rows: int {
194 | res_status := PQresultStatus(query_res);
195 | if res_status == {
196 | case ExecStatusType.PGRES_EMPTY_QUERY;
197 | return false, true, 0;
198 | case ExecStatusType.PGRES_COMMAND_OK;
199 | rows_affected := to_string(PQcmdTuples(query_res));
200 | rows_affected_count := string_to_int(rows_affected);
201 |
202 | return false, true, rows_affected_count;
203 | case ExecStatusType.PGRES_TUPLES_OK;
204 | return true, true, 0;
205 | case ExecStatusType.PGRES_FATAL_ERROR;
206 | error_message := to_string(PQresultErrorMessage(query_res));
207 | log_error("Fatal error: %", error_message);
208 | return false, false, 0;
209 | case ExecStatusType.PGRES_NONFATAL_ERROR;
210 | error_message := to_string(PQresultErrorMessage(query_res));
211 | log_error("Non-fatal error: %", error_message);
212 | return false, false, 0;
213 | case;
214 | log_error("Query result status: %", res_status);
215 | return false, false, 0;
216 | }
217 | }
218 |
219 | get_results :: (query_res: *PGresult, $T: Type, $ignore_unknown := false) -> [] T, success: bool {
220 | raw_result, success := get_results(query_res, type_info(T), ignore_unknown);
221 |
222 | results: []T;
223 | results.data = xx raw_result.data;
224 | results.count = raw_result.count;
225 | return results, success;
226 | }
227 |
228 | Member_Offset :: struct {
229 | member: *Type_Info_Struct_Member;
230 | offset_in_bytes: s64;
231 | }
232 |
233 | get_results :: (query_res: *PGresult, info: *Type_Info, ignore_unknown := false) -> Array_View_64, success: bool {
234 | // @ToDo: Allow to cast single-column results directly to primitive types
235 | assert(info.type == .STRUCT);
236 | info_struct := cast(*Type_Info_Struct) info;
237 |
238 | num_tuples := PQntuples(query_res);
239 | results: Array_View_64;
240 | results.data = alloc(info.runtime_size * num_tuples);
241 | results.count = num_tuples;
242 | initializer := info_struct.initializer;
243 | if !initializer {
244 | memset(results.data, 0, info.runtime_size * num_tuples);
245 | }
246 |
247 | num_columns := PQnfields(query_res);
248 | column_members := NewArray(num_columns, Member_Offset, initialized = false);
249 | defer array_free(column_members);
250 |
251 | for col: 0..num_columns - 1 {
252 | name: string;
253 | name.data = PQfname(query_res, col);
254 | name.count = c_style_strlen(name.data);
255 | member, offset_in_bytes := get_field(info_struct, name);
256 | if !ignore_unknown && !member {
257 | log_error("Column \"%\" has no corresponding member in struct type \"%\"", name, < bool {
291 | if col_type == {
292 | case .INT2;
293 | assert(len == 2);
294 | // Explicitly throwing away 48 bits.
295 | val := ntoh(<< cast(*s16) data);
296 | return write_integer(col, name, info, slot, col_type, val);
297 |
298 | case .OID; #through;
299 | case .INT4;
300 | assert(len == 4);
301 | // Explicitly throwing away 32 bits.
302 | val := ntoh(<< cast(*s32) data);
303 | return write_integer(col, name, info, slot, col_type, val);
304 |
305 | case .INT8;
306 | assert(len == 8);
307 | val := ntoh(<< cast(*s64) data);
308 | return write_integer(col, name, info, slot, col_type, val);
309 |
310 | case .FLOAT4;
311 | if info.type != Type_Info_Tag.FLOAT {
312 | log_error("Error: Trying to write float4 column % into member field \"%\" of type %", col, name, info.type);
313 | return false;
314 | }
315 |
316 | if info.runtime_size == 4 {
317 | << (cast(*float32) slot) = ntoh(<<(cast(*float32) data));
318 | } else {
319 | assert(info.runtime_size == 8);
320 | << (cast(*float64) slot) = cast(float64) ntoh(<<(cast(*float32) data));
321 | }
322 | return true;
323 |
324 | case .FLOAT8;
325 | if info.type != Type_Info_Tag.FLOAT {
326 | log_error("Error: Trying to write float8 column % into member field \"%\" of type %", col, name, info.type);
327 | return false;
328 | }
329 |
330 | if info.runtime_size == 4 {
331 | << (cast(*float32) slot) = cast(float32) ntoh(<<(cast(*float64) data));
332 | } else {
333 | assert(info.runtime_size == 8);
334 | << (cast(*float64) slot) = ntoh(<<(cast(*float64) data));
335 | }
336 | return true;
337 |
338 | case .NUMERIC;
339 | if info.type != Type_Info_Tag.STRING {
340 | log_error("Error: Trying to write numeric column % into member field \"%\" of type %, but we only support writing to string fields at the moment.", col, name, info.type);
341 | return false;
342 | }
343 | success: bool;
344 | << (cast(*string) slot), success = str_from_numeric(data, len, row, col);
345 | return success;
346 |
347 | case .BOOL;
348 | assert(len == 1);
349 | if info.type != Type_Info_Tag.BOOL {
350 | log_error("Error: Trying to write bool column % into member field \"%\" of type %", col, name, info.type);
351 | return false;
352 | }
353 |
354 | val := << cast(*u8) data;
355 | << (cast(*bool) slot) = (val != 0);
356 |
357 | return true;
358 |
359 | case .CHAR; #through;
360 | case .BPCHAR; #through;
361 | case .VARCHAR; #through;
362 | case .NAME; #through;
363 | case .TEXT;
364 | val: string;
365 | val.data = data;
366 | val.count = len;
367 | write_string_value(col, name, info, slot, col_type, val, is_custom = false);
368 | return true;
369 |
370 | case .DATE;
371 | assert(len == 4);
372 | days_since_2000_01_01 := ntoh(<< cast(*s32) data);
373 | if info.type == Type_Info_Tag.INTEGER {
374 | return write_integer(col, name, info, slot, col_type, days_since_2000_01_01);
375 | } else {
376 | #if SUPPORT_DATE_AS_STRINGS {
377 | #import "date"; // https://github.com/rluba/jai-date
378 | date := Date.{2000, 1, 1};
379 | add(*date, days_since_2000_01_01, .DAY);
380 | if info == type_info(Date) {
381 | target := cast(*Date) slot;
382 | < enum_highest_value(Pq_Type) {
460 | // Seems to be a custom type. Try to interpret it as a string
461 | val: string;
462 | val.data = data;
463 | val.count = len;
464 | return write_string_value(col, name, info, slot, col_type, val, is_custom = true);
465 | } else {
466 | log_error("Error: column % type % (with length %) is not yet implemented (for member %)", col, col_type, len, name);
467 | }
468 | return false;
469 | }
470 | }
471 |
472 | write_string_value :: (col: int, name: string, info: *Type_Info, slot: *void, col_type: Pq_Type, value: string, is_custom: bool) -> bool {
473 | if info.type == {
474 | case .STRING;
475 | << (cast(*string) slot) = copy_string(value);
476 | return true;
477 | case .ENUM;
478 | info_enum := cast(*Type_Info_Enum) info;
479 | for info_enum.names {
480 | if it == value {
481 | return write_integer(col, name, info_enum.internal_type, slot, col_type, info_enum.values[it_index]);
482 | }
483 | }
484 |
485 | log_error("Error: Could not find an enum value for value \"%\" of column % for member field \"%\"", value, col, name);
486 | return false;
487 | case;
488 | if is_custom {
489 | log_error("Error: Trying to write custom type of column % into member field \"%\" of type %", col, name, info.type);
490 | } else {
491 | log_error("Error: Trying to write column % of type % into member field \"%\" of type %", col, col_type, name, info.type);
492 | }
493 | return false;
494 | }
495 | }
496 |
497 | write_integer :: (col: int, name: string, info: *Type_Info, pointer: *void, col_type: Pq_Type, value: s64) -> bool {
498 | if info.type != Type_Info_Tag.INTEGER {
499 | log_error("Error: Trying to write column % of type % as an integer into member field \"%\" of type %", col, col_type, name, info.type);
500 | return false;
501 | }
502 | int_info := cast(*Type_Info_Integer) info;
503 |
504 | if int_info.signed {
505 | valid, low, high := Reflection.range_check_and_store(value, int_info, pointer);
506 |
507 | if !valid {
508 | log_error("Value % of column % is out of range for \"%\". (The value must be between % and %.)", value, col, name, low, high);
509 | return false;
510 | }
511 | } else {
512 | valid, low, high := Reflection.range_check_and_store(cast(u64) value, int_info, pointer); // Different overload from the above!
513 |
514 | if !valid {
515 | log_error("Value % of column % is out of range for \"%\". (The value must be between % and %.)", value, col, name, low, high);
516 | return false;
517 | }
518 | }
519 |
520 | return true;
521 | }
522 |
523 | str_from_numeric :: (data: *u8, len: int, row: int, col: int) -> string, success: bool {
524 | if len < 8 {
525 | log_error("Invalid numeric length at row % col %: %", row, col, len);
526 | return "", false;
527 | }
528 |
529 | num_digits := ntoh(<< cast(*u16) data);
530 | weight := ntoh(<< cast(*s16) (data + 2));
531 | sign := ntoh(<< cast(*u16) (data + 4));
532 | dscale := ntoh(<< cast(*u16) (data + 6));
533 | if len != num_digits * 2 + 8 {
534 | log_error("Invalid numeric length at row % col %: %", row, col, len);
535 | return "", false;
536 | }
537 | if dscale > 0x3fff {
538 | log_error("Invalid numeric dscale at row % col %: %", row, col, dscale);
539 | return "", false;
540 | }
541 |
542 | builder: String_Builder;
543 | defer free_buffers(*builder);
544 |
545 | if sign == {
546 | case 0x0000;
547 |
548 | case 0x4000;
549 | append(*builder, "-");
550 |
551 | case 0xc000;
552 | append(*builder, "NaN");
553 |
554 | case;
555 | log_error("Invalid numeric sign at row % col%: %", row, col, sign);
556 | return "", false;
557 | }
558 |
559 | index := 0;
560 | if weight >= 0 && num_digits {
561 | min_digits := 1;
562 | while weight >= 0 && index < num_digits {
563 | digit := ntoh(<< cast(*s16) (data + 8 + index * 2));
564 | print_to_builder(*builder, "%", formatInt(digit, minimum_digits=min_digits));
565 | min_digits = 4;
566 | weight -= 1;
567 | index += 1;
568 | }
569 | while weight >= 0 {
570 | append(*builder, "0000");
571 | weight -= 1;
572 | }
573 | } else {
574 | append(*builder, "0");
575 | }
576 |
577 | if dscale != 0 append(*builder, ".");
578 | dec_len := 0;
579 |
580 | omitted := -1 - weight;
581 | if omitted > 0 {
582 | zeros: int;
583 | if 4 * omitted > cast(s32) dscale {
584 | zeros = dscale;
585 | } else {
586 | zeros = 4 * omitted;
587 | }
588 | print_to_builder(*builder, "%", formatInt(0, minimum_digits=zeros));
589 | dec_len += zeros;
590 | }
591 | min_digits := 4;
592 | while index < num_digits {
593 | digit := ntoh(<< cast(*s16) (data + 8 + index * 2));
594 | if dec_len + 4 > dscale {
595 | remaining := dscale - dec_len;
596 | assert(remaining > 0, "Unexpected number of remaining digits: %", remaining);
597 | for 1..(4 - remaining) digit /= 10;
598 | min_digits = remaining;
599 | }
600 | print_to_builder(*builder, "%", formatInt(digit, minimum_digits=min_digits));
601 | index += 1;
602 | dec_len += min_digits;
603 | }
604 | if (dec_len < dscale) {
605 | print_to_builder(*builder, "%", formatInt(0, minimum_digits=dscale-dec_len));
606 | }
607 |
608 | return builder_to_string(*builder), true;
609 | }
610 |
611 | BOOL_TRUE_VALUE: u8 = 1;
612 | BOOL_FALSE_VALUE: u8 = 0;
613 |
614 | #import "Basic";
615 | #import "Pool";
616 | Reflection :: #import "Reflection";
617 |
618 | #load "byte_order.jai";
619 | #load "pq_types.jai";
620 |
621 | #if OS == .WINDOWS {
622 | Windows :: #import "Windows";
623 | #load "bindings/windows.jai";
624 | size_t :: Windows.size_t;
625 | FILE :: *void;
626 | } else {
627 | #import "POSIX";
628 | #load "bindings/unix.jai";
629 | }
630 |
--------------------------------------------------------------------------------
/pq_types.jai:
--------------------------------------------------------------------------------
1 | // Generated via "generate_types.jai"
2 |
3 | Pq_Type :: enum Oid {
4 | BOOL :: 16;
5 | BYTEA :: 17;
6 | CHAR :: 18;
7 | NAME :: 19;
8 | INT8 :: 20;
9 | INT2 :: 21;
10 | INT2VECTOR :: 22;
11 | INT4 :: 23;
12 | REGPROC :: 24;
13 | TEXT :: 25;
14 | OID :: 26;
15 | TID :: 27;
16 | XID :: 28;
17 | CID :: 29;
18 | OIDVECTOR :: 30;
19 | JSON :: 114;
20 | XML :: 142;
21 | PG_NODE_TREE :: 194;
22 | POINT :: 600;
23 | LSEG :: 601;
24 | PATH :: 602;
25 | BOX :: 603;
26 | POLYGON :: 604;
27 | LINE :: 628;
28 | CIDR :: 650;
29 | FLOAT4 :: 700;
30 | FLOAT8 :: 701;
31 | CIRCLE :: 718;
32 | MACADDR8 :: 774;
33 | MONEY :: 790;
34 | MACADDR :: 829;
35 | INET :: 869;
36 | ACLITEM :: 1033;
37 | BPCHAR :: 1042;
38 | VARCHAR :: 1043;
39 | DATE :: 1082;
40 | TIME :: 1083;
41 | TIMESTAMP :: 1114;
42 | TIMESTAMPTZ :: 1184;
43 | INTERVAL :: 1186;
44 | TIMETZ :: 1266;
45 | BIT :: 1560;
46 | VARBIT :: 1562;
47 | NUMERIC :: 1700;
48 | REFCURSOR :: 1790;
49 | REGPROCEDURE :: 2202;
50 | REGOPER :: 2203;
51 | REGOPERATOR :: 2204;
52 | REGCLASS :: 2205;
53 | REGTYPE :: 2206;
54 | UUID :: 2950;
55 | TXID_SNAPSHOT :: 2970;
56 | PG_LSN :: 3220;
57 | PG_NDISTINCT :: 3361;
58 | PG_DEPENDENCIES :: 3402;
59 | TSVECTOR :: 3614;
60 | TSQUERY :: 3615;
61 | GTSVECTOR :: 3642;
62 | REGCONFIG :: 3734;
63 | REGDICTIONARY :: 3769;
64 | JSONB :: 3802;
65 | JSONPATH :: 4072;
66 | REGNAMESPACE :: 4089;
67 | REGROLE :: 4096;
68 | REGCOLLATION :: 4191;
69 | PG_MCV_LIST :: 5017;
70 | PG_SNAPSHOT :: 5038;
71 | XID8 :: 5069;
72 | }
73 |
74 | Pq_Array_Type :: enum Oid {
75 | BOOL :: 1000;
76 | BYTEA :: 1001;
77 | CHAR :: 1002;
78 | NAME :: 1003;
79 | INT8 :: 1016;
80 | INT2 :: 1005;
81 | INT2VECTOR :: 1006;
82 | INT4 :: 1007;
83 | REGPROC :: 1008;
84 | TEXT :: 1009;
85 | OID :: 1028;
86 | TID :: 1010;
87 | XID :: 1011;
88 | CID :: 1012;
89 | OIDVECTOR :: 1013;
90 | JSON :: 199;
91 | XML :: 143;
92 | POINT :: 1017;
93 | LSEG :: 1018;
94 | PATH :: 1019;
95 | BOX :: 1020;
96 | POLYGON :: 1027;
97 | LINE :: 629;
98 | CIDR :: 651;
99 | FLOAT4 :: 1021;
100 | FLOAT8 :: 1022;
101 | CIRCLE :: 719;
102 | MACADDR8 :: 775;
103 | MONEY :: 791;
104 | MACADDR :: 1040;
105 | INET :: 1041;
106 | ACLITEM :: 1034;
107 | BPCHAR :: 1014;
108 | VARCHAR :: 1015;
109 | DATE :: 1182;
110 | TIME :: 1183;
111 | TIMESTAMP :: 1115;
112 | TIMESTAMPTZ :: 1185;
113 | INTERVAL :: 1187;
114 | TIMETZ :: 1270;
115 | BIT :: 1561;
116 | VARBIT :: 1563;
117 | NUMERIC :: 1231;
118 | REFCURSOR :: 2201;
119 | REGPROCEDURE :: 2207;
120 | REGOPER :: 2208;
121 | REGOPERATOR :: 2209;
122 | REGCLASS :: 2210;
123 | REGTYPE :: 2211;
124 | UUID :: 2951;
125 | TXID_SNAPSHOT :: 2949;
126 | PG_LSN :: 3221;
127 | TSVECTOR :: 3643;
128 | TSQUERY :: 3645;
129 | GTSVECTOR :: 3644;
130 | REGCONFIG :: 3735;
131 | REGDICTIONARY :: 3770;
132 | JSONB :: 3807;
133 | JSONPATH :: 4073;
134 | REGNAMESPACE :: 4090;
135 | REGROLE :: 4097;
136 | REGCOLLATION :: 4192;
137 | PG_SNAPSHOT :: 5039;
138 | XID8 :: 271;
139 | }
140 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | # Mimimal Postgresql client for Jai
2 |
3 | This module contains `libpq` bindings as well as some higher-level functions for…
4 | * … connecting to a Postgresql database,
5 | * … executing parameterized queries and
6 | * … optionally parsing the results into a typed array.
7 |
8 | It only supports synchronous execution for now.
9 |
10 | ## Usage
11 |
12 | First connect to a Postgresql instance using a connection uri string:
13 |
14 | ```Jai
15 | connection_str := "postgres://:@:/";
16 | conn, success := connect(connection_str);
17 | defer disconnect(conn);
18 | ```
19 |
20 | Then use `conn` to execute statements…
21 |
22 | ```Jai
23 | // Restrict schema access
24 | success = execute(conn, "SELECT pg_catalog.set_config('search_path', 'public', false)");
25 | ```
26 |
27 | … or execute a query and automatically parse the result into an array of any given type:
28 |
29 | ```Jai
30 | query :: #string END
31 | SELECT * FROM tourist_attractions
32 | WHERE city = $1 AND price < $2 AND min_age < $3
33 | ORDER BY name
34 | END
35 |
36 | Attraction :: struct {
37 | name: string;
38 | city: string;
39 | price: string; // Numerics are parsed into strings
40 | min_age: int;
41 | is_open: bool;
42 | }
43 |
44 | // Query parameters can be passed as variadic args after `query`:
45 | city := "Vienna";
46 | price := 50;
47 | min_age := 18;
48 | attractions, success = execute(conn, Attraction, query, city, price, min_age);
49 | ```
50 |
51 | By default, `execute` fails if the result contains a column that has no corresponding member in the struct type you passed.
52 | You can pass `ignore_unknown = true` to `execute(…)` to ignore unknown columns instead.
53 |
54 | If your struct type contains members that aren’t present in the result (or are null), they will be left at their default values.
55 |
56 | ## Memory model
57 |
58 | You’re responsible for freeing everything (including strings) in the result array.
59 | Using a Pool allocator around your `execute` calls might be a good idea.
60 |
61 | ## Supported query parameter types
62 |
63 | * All integer types
64 | * `float` and `float64`
65 | * `string`
66 | * `bool`
67 |
68 | ## Supported return value column types
69 |
70 | * `INT2`, `INT4`, `INT8`
71 | * `FLOAT4`, `FLOAT8`
72 | * `NUMERIC` (currently only parsed into `string` fields)
73 | * `BOOL`
74 | * `CHAR`, `BPCHAR`, `VARCHAR`, `NAME`, `TEXT`
75 | * `DATE`, `TIMESTAMP`, `TIMESTAMPTZ`
76 | * `BYTEA`
77 | * `OID`
78 | * `UUID`
79 | * Custom enum types (`CREATE TYPE … AS ENUM`), which can be parsed into `string` or `enum` member fields.
80 |
--------------------------------------------------------------------------------