Things I want in a modern relational query language

This was a very old draft I’ve had sitting around for years. The recent discussions of new query languages like Acadia spurred me to revisit, revise, and publish this.

I think one of the biggest causes of NoSQL is that while SQL is a powerful language because of the ideas behind it, it’s often implemented in clumsy and archaic ways. A language that learns from SQL could make relational data better to manipulate for programmers. I’ll try to think of things similar to those that I have dealt with in real-world situations and how a better query language could have helped. I’d love discussion on what else could be done.

For what it’s worth, my background with RDBMSes is mostly in MySQL and Db2, but I have used SQLite, SQL Server, Oracle, and Postgres in anger enough (in descending order of familiarity).

Better syntax

I’m not picky myself about aesthetics, but many others are. Programmers are like toddlers, they want their Kraft Dinner and not the broccoli. Basing syntax off of PL/I is a 1970’s IBM choice that probably wouldn’t fly today. Due to popular demand, such a language probably would pick up C or Python aesthetics syntactically, though perhaps with some ML or Prolog influence (as i.e. Rust shows).

With better syntax I hope can come better parsers. I especially loathe MySQL’s parser, which never actually tells you where problems lie or what it is, if it isn’t some syntax absurdity like DELIMITER. Better SQL parsers do exist in conventional implementations though – Oracle is surprisingly good at reporting errors by telling you what it expects.

The examples I write are just for show; I’m not wed to anything nor do I demand what syntax must be. My influences in these examples are most likely from F# (ML family), Erlang (Prolog-esque), and Elixir (Erlang and Ruby like).

A functional programming language that isn’t hostile to functional programming

SQL’s 4GL qualities where you describe how you want your data instead of looping over it by hand is SQL’s most powerful weapon. This is pretty close to a lot of functional programming paradigms like lazy evaluation – hello Haskell. Unfortunately, the standard library of most SQL dialects is somewhat anemic on this front; being optimized for 1980’s procedural programs. Most SQL dialects ended up supporting stored procedures, which are inherently… procedural; going against the grain of SQL’s declarative nature. This ends up reflected in most user SQL code, where they imitate the style that the language and standard library make easy, which involves a lot of dealing with mutable state (cursors…) and procedures over functions. Defaults matter.

Less opaque query planners

While being a 4GL is a strength with how powerful compilers and optimizers are optimizing most code, it can be easy to make a mistake that makes a query more expensive, but planners can be cryptic unless you’re already an SQL optimization expert. (Again, special mention to how bad MySQL’s “explain”ing tools are for this.) While not strictly PLT related, it is something weak in current SQL implementations that computer scientists have learned a lot about.

Better user defined types

While some RDBMSes offer the concept of domains for specifying user-defined data types (and is an optional part of the SQL spec), they can be limited in what they can do (usually just sugar around ranges or checks). Postgres was the only one that seems to support it; Oracle apparently only got support recently (though it seems perhaps more flexible than Postgres). Unfortunately, I haven’t used either enough to be very familiar with how it works in practice. However, domains are covered in Codd’s The Relational Model, which is the foundational text for RDBMSes. Considering Postgres’ heritage in Ingres, which was based on QUEL, which in turn was closer to Codd’s vision of RDBMSes than SQL was, it makes sense Postgres ended up following that.

Sum types, discriminated unions, and pattern matching

One schema that illustrates how modern functional programming techniques could be applied here is this function that returns stack frame information. For context, IBM i, the operating system mentioned here, provides many SQL functions for system administration under the “Services” umbrella. While this is very useful for DBAs-turned-system administrators in the heat of debugging, it is unfortunately clumsy, because effectively there’s “groups” of columns that are effectively mutually exclusive, lots of nullables because of that, and string fields that are effectively enums.

Some of these are just poor schema design (perhaps not helped by the fact it must be returned in a single table – returning multiple tables would also be an interesting direction to go in); the stringy enums can be fixed with a foreign key constraint on a table that acts as an enum. Some are down to language expressiveness in implementations, though.

Using this idea, I try to come up with a better example that would make queries less verbose and error-prone:

// heavily omitting things for simplicity; i.e displacement or additional enum cases, as well as defining enums ad-hoc (they could be declared out of the type too)

// Each frame type, while similar, is not identical, and has different
// semantics or qualifications.
type MachineInterfaceInfo =
{
ActivationGroup: long;
ASP: long;
Library: string;
}

// For those that lack context here, IBM i supports multiple program models:
// - Java programs, which runtime provides the system some special insight
// - OPM programs, the old managed runtime program ABI
// - ILE programs, the new managed runtime program ABI
// - AIX programs, through syscall emulation
// - LIC, the IBM i kernel
// It can generate stack traces for all these kinds of programs; some programs
// may have a call stack containing a frame entry of each type.

type FrameType =
// Inherit fields from another record type.
| ILE { MachineInterfaceInfo | ServiceProgram: string; Module: string; }
| OPM { MachineInterfaceInfo | Program: string; }
| AIX { Bitness: enum(32 | 64); LibArchive: Option(string); Module: string, Syscall: bool; }
| Java { MethodType: enum(DirectExecution | Glue | Interp | JIT | MMI); ClassName: string; Signature: Option(string); }

table Frame =
{
ThreadID: long;
FrameType: FrameType;
Function: Option(string);
}

function StackInfo(JobID: string) : Frame;

// An SQL-like select with pattern matching to filter.
select Function from StackInfo("1234/JOB/5678") where AIX { Bitness: 64 } = FrameType;
// this would return FrameType of ILE and OPM
select Function from StackInfo("1234/JOB/5678") where MachineInterfaceInfo { Library: "QSYS" } = FrameType;
select Function from StackInfo("1234/JOB/5678") where AIX { LibArchive: "libc.a" } = FrameType;
select Function from StackInfo("1234/JOB/5678") where AIX { LibArchive: None } = FrameType;

// A function that prints information with a pattern match inside of it.
function FrameFullySpecifiedProgramName(frame : Frame) : string =
match frame.FrameInfo with
| OPM { Program: program } -> program
| ILE { ServiceProgram: srvpgm, Module: module } -> "#{srvpgm}/#{module}"
| AIX { LibArchive: None, Module: module } -> module
| AIX { LibArchive: lib, Module: module } -> "#{lib}(#{module})"
| Java { ClassName: class } -> class
// we must match all possible types, or discard with _
| _ -> "?"

// A function that uses pattern matching based overloads and destructuring.
function FrameJavaFunctionDef(frame : Frame { Java { Signature: None } = .FrameInfo }) : string =
"#{frame.Function}()"

function FrameJavaFunctionDef(frame : Frame { Java { Signature: signature } = .FrameInfo }) : string =
"#{frame.Function}(#{signature})"
// A call to this with a non-Java frame is an error, because no patterns could match.

If we can collapse the mutually exclusive set of columns, it also makes it much easier to visualize too. A lot less scrolling left and right if they can i.e. be turned into subcolumns shown per row in a larger column, or as a strings displayed differently per type.

Foreign keys that match on multiple types

Say I have tables “Software”, “Version”, and “Download” (a sort of WEMI-ish hierarchy), and that each could have images, with a “Picture” table. (Because the images themselves have metadata, they’re a table rather than a column on each of these.) Usually, you would use a many-to-many table for each kind of relation, so “SoftwarePicture”, “VersionPicture”, etc. This seems like pointless duplication, if instead we could have a many to many table that effectively has a discriminated union on foreign keys:

table ObjectPictures =
{
// a foreign key is assumed to have the same type as what it relates to
PictureID: key relates to (Picture.PictureID);
ObjectID: key relates to (Software.SoftwareID | Version.VersionID | Download.DownloadID);
}

insert into ObjectPictures (PictureID, ObjectID) values (0x1234, DownloadID { 0x1234 });

Chasing down why installing the kernel segfaulted

I’ve been running a server for continuous integration targeting a specific architecture. If you’ve been running servers recently, you’ll know about the constant treadmill of kernel patches due to widely publicized issues like Copy Fail. Usually, these go pretty smoothly (except the previous kernel, which had a PowerPC specific build regression). Now this time, when running make install for the kernel, I noticed a very curious message:

# make install
  INSTALL /boot
/usr/bin/dracut: line 3125: 3644490 Segmentation fault         hardlink "$initdir" 2>&1
     3644491 Done                       | ddebug
Generating grub configuration file ...
[...]

That’s pretty concerning. Now I’m worried about if this will even work when I reboot into it. Let’s try to get more detail; the kernel makefile uses V=1 to show extra information.

# make install V=1 
make --no-print-directory -C /usr/src/linux-6.18.32-gentoo-r1 \
-f /usr/src/linux-6.18.32-gentoo-r1/Makefile install
# INSTALL /boot
  unset sub_make_done; ./scripts/install.sh
/usr/bin/dracut: line 3125: 3648755 Segmentation fault         hardlink "$initdir" 2>&1
     3648756 Done                       | ddebug
Generating grub configuration file ...

Well, that didn’t tell us much except it runs a script for the actual install part. Let’s take a look at the relevant part.

# User/arch may have a custom install script
for file in "${HOME}/bin/${INSTALLKERNEL}"              \
            "/sbin/${INSTALLKERNEL}"                    \
            "${srctree}/arch/${SRCARCH}/install.sh"     \
            "${srctree}/arch/${SRCARCH}/boot/install.sh"
do
        if [ ! -x "${file}" ]; then
                continue
        fi

        # installkernel(8) says the parameters are like follows:
        #
        #   installkernel version zImage System.map [directory]
        exec "${file}" "${KERNELRELEASE}" "${KBUILD_IMAGE}" System.map "${INSTALL_PATH}"
done

This is effectively a wrapper around installkernel, which is a custom distribution-specific program (the kernel supplies a generic equivalent that’s not as good if you lack it) that handles things like generating an initrd (in this case, delegating that to dracut) and updating the boot loader. In this case, Gentoo’s version takes a -v flag, so let’s add that and see if we can get more interesting information:

exec "${file}" -v "${KERNELRELEASE}" "${KBUILD_IMAGE}" System.map "${INSTALL_PATH}"

OK, let’s run make install V=1 again:

dracut[I]: *** Hardlinking files ***
/usr/bin/dracut: line 3125: 3403396 Segmentation fault         hardlink "$initdir" 2>&1
     3403397 Done                       | ddebug
dracut[I]: *** Hardlinking files done ***

Well, it looks like it’s contained in this specific step. It seems we need to take a look at dracut itself. The relevant chunk (with line 3125 annotated):

# Hardlink is mtime-sensitive; do it after the above clamp.
if [[ $do_hardlink == yes ]] && command -v hardlink > /dev/null; then
    dinfo "*** Hardlinking files ***"
    hardlink "$initdir" 2>&1 | ddebug
    dinfo "*** Hardlinking files done ***"

    # Hardlink itself breaks mtimes on directories as we may have added/removed
    # dir entries. Fix those up.
    if [[ ${SOURCE_DATE_EPOCH-} ]] && [[ $CPIO != 3cpio ]]; then
        clamp_mtimes "$initdir" -type d
    fi
fi # this is line 3125

It’s the fi that ends this block. Anyways, hardlink is clearly implicated here. A nicer person would replace hardlink on the path, but in this case, I’m just going to modify the dracut executable. (Don’t do this at home!) I’ll invoke this with gdb instead; and remove the piping to dracut’s debug logging:

    #hardlink "$initdir" 2>&1 | ddebug
    gdb --args hardlink "$initdir"

Time to run make again. When we get the gdb prompt, let’s run it:

dracut[I]: *** Hardlinking files ***
GNU gdb (Gentoo 17.1 vanilla) 17.1
Copyright (C) 2025 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "powerpc64-unknown-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<https://bugs.gentoo.org/>.
Find the GDB manual and other documentation resources online at:
    <http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from hardlink...
Reading symbols from /usr/lib/debug/usr/bin/hardlink.debug...
(gdb) catch signal 
Catchpoint 1 (standard signals)
(gdb) run
Starting program: /usr/bin/hardlink /var/tmp/dracut.dEcmlh1/initramfs
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/usr/lib64/libthread_db.so.1".

Program terminated with signal SIGSEGV, Segmentation fault.
The program no longer exists.
(gdb) 

Wait, where’d the program even go? gdb needs the process to exist still to inspect it and- wait, did the kernel kill it perhaps? If we check dmesg…

[1199626.054903] BUG: Unable to handle kernel data access at 0xc0403effffffffc8
[1199626.054921] Faulting instruction address: 0xc000000000396cb4
[1199626.054927] Oops: Kernel access of bad area, sig: 11 [#15]
[1199626.054932] BE PAGE_SIZE=4K MMU=Hash  SMP NR_CPUS=32 NUMA pSeries
[1199626.054939] Modules linked in: vsock_diag vmx_crypto ibmveth pseries_rng rng_core fuse vsock_loopback vmw_vsock_virtio_transport_common vsock sr_mod cdrom nx_crypto
[1199626.054969] CPU: 22 UID: 0 PID: 3545486 Comm: hardlink Tainted: G S    D             6.18.26-gentoo-ppc #1 VOLUNTARY 
[1199626.054981] Tainted: [S]=CPU_OUT_OF_SPEC, [D]=DIE
[1199626.054984] Hardware name: IBM,8286-42A POWER8 (architected) 0x4b0201 0xf000004 of:IBM,FW860.90 (SV860_226) hv:phyp pSeries
[1199626.054992] NIP:  c000000000396cb4 LR: c000000000396e68 CTR: c000000000396e40
[1199626.054998] REGS: c00000018a4a7840 TRAP: 0380   Tainted: G S    D              (6.18.26-gentoo-ppc)
[1199626.055006] MSR:  8000000000009032 <SF,EE,ME,IR,DR,RI>  CR: 44002242  XER: 20000000
[1199626.055023] CFAR: c000000000396e64 IRQMASK: 0 
                 GPR00: c0003d00002b0acc c00000018a4a7ae0 c0000000018ad100 fffffffffffffff0 
                 GPR04: fffffffffffffff0 0000000000000001 c000000637bc0c28 0000000000000000 
                 GPR08: 0000001ffd4cd000 c0003f0000000000 0000000000000000 c0003d00002b48a8 
                 GPR12: c000000000396e40 c00000002ec44800 0000000000000000 0000000000000000 
                 GPR16: 0000000000000000 0000000000000000 0000000000000000 0000000000000000 
                 GPR20: 0000000000000000 0000000000000000 0000000000000000 00000001000300c8 
                 GPR24: 000000010002efe0 0000000000000000 0000000000000001 c00000011d91a480 
                 GPR28: 0000000000000000 c00000000271cd00 c0003d00002b80b8 c0403effffffffc0 
[1199626.055106] NIP [c000000000396cb4] __ksize+0x34/0x190
[1199626.055117] LR [c000000000396e68] kfree_sensitive+0x28/0x80
[1199626.055124] Call Trace:
[1199626.055127] [c00000018a4a7ae0] [c00000018a4a7b20] 0xc00000018a4a7b20 (unreliable)
[1199626.055136] [c00000018a4a7b10] [c00000018a4a7b80] 0xc00000018a4a7b80
[1199626.055143] [c00000018a4a7b40] [c0003d00002b0acc] nx_crypto_ctx_shash_exit+0x24/0x60 [nx_crypto]
[1199626.055154] [c00000018a4a7b70] [c00000000097af78] crypto_shash_exit_tfm+0x28/0x40
[1199626.055165] [c00000018a4a7b90] [c00000000096f168] crypto_destroy_tfm+0x98/0x140
[1199626.055176] [c00000018a4a7bd0] [c000000000978d60] crypto_exit_ahash_using_shash+0x20/0x40
[1199626.055186] [c00000018a4a7bf0] [c00000000096f168] crypto_destroy_tfm+0x98/0x140
[1199626.055196] [c00000018a4a7c30] [c000000000998b5c] hash_release+0x1c/0x30
[1199626.055207] [c00000018a4a7c50] [c000000000996f58] alg_sock_destruct+0x38/0x60
[1199626.055216] [c00000018a4a7c80] [c0000000010bed98] __sk_destruct+0x48/0x2b0
[1199626.055227] [c00000018a4a7cc0] [c0000000009970a8] af_alg_release+0x58/0xb0
[1199626.055237] [c00000018a4a7cf0] [c0000000010b3918] __sock_release+0x68/0x150
[1199626.055247] [c00000018a4a7d70] [c0000000010b3a20] sock_close+0x20/0x40
[1199626.055257] [c00000018a4a7d90] [c0000000004549b0] __fput+0x110/0x3a0
[1199626.055265] [c00000018a4a7de0] [c00000000044df48] sys_close+0x48/0xa0
[1199626.055275] [c00000018a4a7e10] [c000000000029d40] system_call_exception+0x140/0x2d0
[1199626.055284] [c00000018a4a7e50] [c00000000000c354] system_call_common+0xf4/0x258
[1199626.055295] ---- interrupt: c00 at 0x3ffff7def394
[1199626.055300] NIP:  00003ffff7def394 LR: 00003ffff7def3f0 CTR: 0000000000000000
[1199626.055305] REGS: c00000018a4a7e80 TRAP: 0c00   Tainted: G S    D              (6.18.26-gentoo-ppc)
[1199626.055312] MSR:  800000000280f032 <SF,VEC,VSX,EE,PR,FP,ME,IR,DR,RI>  CR: 24002242  XER: 00000000
[1199626.055334] IRQMASK: 0 
                 GPR00: 0000000000000006 00003fffffffb820 00003ffff7f87100 0000000000000003 
                 GPR04: 0000000000000000 0000000000000000 0000000000000000 0000000000000000 
                 GPR08: 0000000000000000 0000000000000000 0000000000000000 0000000000000000 
                 GPR12: 0000000000000000 00003ffff7ff37e0 0000000000000000 0000000000000000 
                 GPR16: 0000000000000000 0000000000000000 0000000000000000 0000000000000000 
                 GPR20: 0000000000000000 0000000000000000 0000000000000000 00000001000300c8 
                 GPR24: 000000010002efe0 0000000000000000 0000000000000001 0000000100030070 
                 GPR28: 00003fffffffc078 0000000000000002 00003ffff7f802d8 00000001000300c8 
[1199626.055412] NIP [00003ffff7def394] 0x3ffff7def394
[1199626.055417] LR [00003ffff7def3f0] 0x3ffff7def3f0
[1199626.055421] ---- interrupt: c00
[1199626.055425] Code: 38426480 28230010 418200c4 3d2200df fbe1fff8 f821ffd1 787fa402 7c641b78 3929c820 7bff3664 e9290000 7fe9fa14 <e95f0008> 71480001 408200a4 895f0030 
[1199626.055456] ---[ end trace 0000000000000000 ]---

[1199626.059073] note: hardlink[3545486] exited with irqs disabled

Oh no. Why is hardlink causing a kernel oops, and why is it doing it in the crypto subsystem? The kernel’s new nemesis AF_ALG shows up, best known for… Copy Fail. This almost certainly isn’t Copy Fail, but I wouldn’t be surprised if the fix for Copy Fail may have introduced a regression. Let’s figure out why hardlink is even using this. Instead of gdb, let’s try putting it under strace in our hacked up dracut. In our horribly long syscall trace:

close(5)                                = 0
close(0)                                = 0
close(0)                                = -1 EBADF (Bad file descriptor)
close(4)                                = 0
close(3)                                = ?
+++ killed by SIGSEGV +++
/usr/bin/dracut: line 3128: 3561819 Segmentation fault         (core dumped) strace hardlink "$initdir"

Well, the kernel oopsed in the middle of the close syscall, giving us a very funny SIGSEGV. What’s the last thing that created an fd #3?

socket(AF_ALG, SOCK_SEQPACKET, 0)       = 3
bind(3, {sa_family=AF_ALG, salg_type="hash", salg_feat=0, salg_mask=0, salg_name="sha256"}, 88) = 0
accept(3, NULL, NULL)                   = 4

Oh great, it actually is using AF_ALG. Why would something that makes hardlinks want to use the kernel’s buggy crypto acceleration path? It’s not IPsec, after all. Well, if we look for AF_ALG in util-linux, the package where hardlink comes from, there’s a utility function for file comparisons (in lib/fileeq.c). If we look at the first big comment:

/*
 * compare file contents
 *
 * The goal is to minimize amount of data we need to read from the files and be
 * ready to compare large set of files, it means reuse the previous data if
 * possible. It never reads entire file if not necessary.
 *
 * The other goal is to minimize number of open files (imagine "hardlink /"),
 * the code can open only two files and reopen the file next time if
 * necessary.
 *
 * This code supports multiple comparison methods. The very basic step which is
 * generic for all methods is to read and compare an "intro" (a few bytes from
 * the beginning of the file). This intro buffer is always cached in 'struct
 * ul_fileeq_data', this intro buffer is addressed as block=0. This primitive
 * thing can reduce a lot ...
 *
 * The next steps depend on selected method:
 *
 *  * memcmp method: always read data to userspace, nothing is cached, directly
 *  compare file contents; fast for small sets of small files.
 *
 *  * Linux crypto API: zero-copy method based on sendfile(), data blocks are
 *  sent to the kernel hash functions (sha1, ...), and only hash digest is read
 *  and cached in userspace. Fast for large set of (large) files.
 *
 * [...]
 */

Cool. It’s an optimization path that exposes a fragile kernel subsystem to just do… hashing. The actual bit that sets up the socket in that file is in init_crypto_api, and the logic to use it is gated behind a USE_FILEEQ_CRYPTOAPI define. Since there’s a fallback back, can we disable this easily to use the memcmp behaviour instead, which certainly should be OK? Well, if we check include/fileeq.h, which exposes the API surface for that:

#if defined(__linux__) && defined(HAVE_LINUX_IF_ALG_H)
# define USE_FILEEQ_CRYPTOAPI 1
#endif

Nice, it’s hardcoded to always be available with newer kernels effectively; no build system options (and thus no USE flags either). Well, let’s just turn it off. Since I’m using Gentoo on this CI server, it’s trivial to fix this. Put a patch containing the below into /etc/portage/patches/sys-apps/util-linux/no-af-alg.patch and rebuild the package with emerge -av sys-apps/util-linux:

diff --git a/include/fileeq.h b/include/fileeq.h
index 90b8d5118..e4d2dfae2 100644
--- a/include/fileeq.h
+++ b/include/fileeq.h
@@ -11,7 +11,7 @@
 #include <stdbool.h>

 #if defined(__linux__) && defined(HAVE_LINUX_IF_ALG_H)
-# define USE_FILEEQ_CRYPTOAPI 1
+#// define USE_FILEEQ_CRYPTOAPI 1
 #endif

 /* Number of bytes from the beginning of the file we always

With our strace invocation still in our hacked up dracut, let’s run it again:

close(3)                                = 0                             
close(0)                                = 0                             
close(0)                                = -1 EBADF (Bad file descriptor)
fstat(1, {st_mode=S_IFCHR|0600, st_rdev=makedev(0x88, 0x2), ...}) = 0                                                                                    
write(1, "Mode:                     real\n", 31Mode:                     real                                                                            
) = 31                                                                      
write(1, "Method:                   memcmp"..., 33Method:                   memcmp                                                                       
) = 33                                                                                                                                                   
write(1, "Files:                    1038\n", 31Files:                    1038                                                                            
) = 31                                                                                                                                                   
write(1, "Linked:                   3 file"..., 34Linked:                   3 files                                                                      
) = 34                                                                      
write(1, "Compared:                 0 xatt"..., 35Compared:                 0 xattrs                                                                     
) = 35                                                                      
write(1, "Compared:                 416 fi"..., 36Compared:                 416 files                                                                    
) = 36                                                                                                                                                   
write(1, "Saved:                    5.74 K"..., 35Saved:                    5.74 KiB                                                                     
) = 35                                                                                                                                                   
write(1, "Duration:                 1.0702"..., 43Duration:                 1.070276 seconds                                                             
) = 43                                                                      
exit_group(0)                           = ?                                                                                                              
+++ exited with 0 +++                                                       
dracut[I]: *** Hardlinking files done *** 

Yay, it works. It looks like it also bombed out near the end doing cleanup and making a final report, so it turns out it probably would have worked all along. Now I need to figure out why the kernel oopsed at all…

(I also suspect that in the efforts to defang AF_ALG, kernel people will make it not be zero copy in the future, rendering util-linux using it moot. Patches to remove AF_ALG from util-linux may be a good idea.)

Nonsense “template with C linkage” errors from GCC on some platforms

If you’re dealing with errors like this compiling a C++ project that uses CMake:

/QOpenSys/pkgs/lib/gcc/powerpc-ibm-aix6.1.0.0/10/include/c++/bits/memoryfwd.h:63:3: error: template with C linkage
   63 |   template<typename>
      |   ^~~~~~~~

…it’s because CMake is passing includes with -isystem instead of -I. On some platforms (i.e. AIX), GCC will assume any header that comes from -isystem is C only (as it may be some funny vendor C++ dialect), and will automatically implicitly wrap them in extern "C", which is why you see the error in spite of not seeing extern "C" anywhere in the source.

For CMake, the easiest way to fix thisis to remove SYSTEM from usages of target_include_directories. However, this doesn’t seem to guarantee it; I’ve had things marked with just INTERFACE end up using -isystem. In that case, then you can add this property, and anything that comes after will always use -I:

set(CMAKE_NO_SYSTEM_FROM_IMPORTED TRUE)

Note that it may also be possible to rebuild GCC to remove this assumption as well.

A few design decisions for a new chat platform

Discord has recently made a controversial decision that could be the start of long-term decline. The situation has people looking at alternatives. However, a lot of the alternatives I feel bark up the wrong tree, or are good for certain niches, but not others (i.e. I like Zulip for focused technical communities, but it’s a bad fit for social clubs). In this post, I’ll try to outline things I feel a lot of alternatives get wrong (or ideally, get right). There’s a lot of navel gazing and bikeshedding about this topic, so I’ll stick with what I think are my most notable takes.

Continue reading

Brief thoughts on Showing Up

Showing Up is a 2023 slice-of-life movie directed by Kelly Reichardt, about the day to day troubles of a sculptor named Lizzy. I quite liked it. Most people talk about the movie commenting on the artistic process (i.e. a kiln gone wrong and her disappointment, while others suggest embracing the flaws). However, what struck me the most was the protagonist’s struggles with others taking responsibility. Spoilers below!

Continue reading

The Brave Little Toaster as a Horror Film Series

The Brave Little Toaster film series, covers some rather adult topics for a film series ultimately intended for kids. How many children’s films can you think of that feature abandonment, loneliness, self-sacrifice, self-worth, fate, and, most shocking of all, suicide?

I will not be discussing every story beat as at this point I am sure most of y’all are familiar with the hero’s journey and the structure of a story.

Tips for installing Windows 98 (and other old versions) in QEMU/UTM

Windows 98 runs surprisingly well in QEMU via UTM SE, but it requires some care in setting it up. It’s a great way to run old 90s Windows and DOS software on your iPad (and Mac too, though you have other options available to you, or an iPhone if you don’t mind the HID difficulties).

This post provides some suggestions and tips for installing Windows and selecting the best emulated devices. The guidance is intended for UTM users on Apple platforms, but should apply to anything QEMU based (or QEMU itself). The advice might also be useful for other operating systems in UTM/QEMU as well.

Windows 95 on UTM SE on an iPad Pro with Magic Keyboard. Note you're better off using Windows 98, but this does work as well.
Continue reading

Very brief thoughts on The Magnificent Ambersons

I found the film interestingly transitional. While it’s surprising how modern Citizen Kane feels for a film of its time, Ambersons feels simultaneously likewise ahead of its time, yet also dated. There are many interesting and dynamic cinematography decisions (mirror shots, towering stairwell shots, the children fighting in the beginning. But the pacing is plodding and feels more remiscient of a film from the 1930’s. It feels like it’s from before Kane rather than after. Considering the amount that was cut, I’m skeptical of the claims of the intended cut being superior. That said, there’s a lot of confusing cuts and time advancements – perhaps more connective tissue between scenes would have made those smoother.

Continue reading

Notes when disassembling Fujitsu Siemens Futro S200/S300 thin clients

The disassembly instructions found online (i.e. from here) only let you access the board. If you need to remove the board (i.e. inspect the rear or replace it), then there are some things to keep in mind:

  • The D-sub nuts for serial/parallel/VGA hold the case together, as does the rear 3.5mm jack. The retention nut on it can be hard to remove.
  • There are only three screws holding the board down on the corners.
  • You likely don’t need to remove the heatsink except to replace thermal paste (which it uses despite using thermal pads everywhere else). It looks like it retains the board to the chassis, but it doesn’t; the standoffs the heatsink is screwed into just float and aren’t held to the chassis. I suspect it’s solely for avoiding PCB flex when screwing the heatsink on.
    • If you unscrew both the heatsink and the board, the standoffs will fall out of place and float around in the chassis until you find them again. One thing at a time.
  • Likely, you don’t need to remove the GPU heatsink (small black one). It doesn’t have any retention involved.
  • You may not need to remove the PCI bracket, but you might find removing it helps.
  • You don’t need to remove the PSU; the board lifts around it. Obviously, unplug the power connector.
  • There’s an “EMI gasket” between the ethernet port and the chassis. Remove it temporarily; it can be reinstalled.
  • There is electrical tape on the front and rear USB ports. Remove these and don’t bother reinstalling them.
  • The board tilts upward from the front ports, then out. Friction is what keeps the board in mostly after removing the screws.
Continue reading