Skip to content

Conversation

@coenttb
Copy link

@coenttb coenttb commented Aug 23, 2025

Summary

This PR introduces proper boolean type handling in StructuredQueries to better support databases with native boolean types (like PostgreSQL) while maintaining backward compatibility with SQLite.

Problem

Previously, boolean values were always encoded as integers (0/1), which caused type mismatch errors when working with PostgreSQL's native BOOLEAN type:

column "isActive" is of type boolean but expression is of type bigint

Solution

  • Add a new .bool(Bool) case to the QueryBinding enum
  • Allow database drivers to handle boolean values appropriately for their specific database
  • Maintain backward compatibility with SQLite

Changes

Core Features

  • Add .bool(Bool) case to QueryBinding enum
  • Update Bool's queryBinding to use .bool(self) instead of .int()
  • Add boolean handling in debugDescription (returns "1"/"0" for SQLite compatibility)

SQLite Support

  • SQLite driver converts .bool to integer (0/1) for compatibility
  • All existing tests pass without modification

Trigger Support

  • Add proper boolean handling in trigger statements

Additional Improvements

  • Make iso8601String property public on Date extension
  • Make quoted method public in Quoting extension
  • These visibility changes allow database drivers to implement their own database-specific SQL rendering methods without duplicating utility functions

Impact

  • Backward Compatible: SQLite continues to work exactly as before
  • PostgreSQL Support: Native boolean types now work correctly
  • Extensible: Other database drivers can implement their preferred boolean handling
  • Type Safe: Maintains type safety throughout the library

Testing

All existing tests pass. The change is backward compatible and does not break existing SQLite-based code.

Related

This change enables proper PostgreSQL support in downstream libraries like swift-structured-queries-postgres.

This change introduces proper boolean type handling in StructuredQueries
to better support databases with native boolean types (like PostgreSQL)
while maintaining backward compatibility with SQLite.

## Changes

### Core Features
- Add `.bool(Bool)` case to `QueryBinding` enum
- Update `Bool`'s `queryBinding` to use `.bool(self)` instead of `.int()`
- Add boolean handling in `debugDescription` (returns "1"/"0" for SQLite compatibility)

### SQLite Support
- SQLite driver converts `.bool` to integer (0/1) for compatibility
- Tests continue to work with existing SQLite behavior

### Trigger Support
- Add proper boolean handling in trigger statements

### Additional Improvements
- Make `iso8601String` property public on Date extension
- Make `quoted` method public in Quoting extension

These visibility changes allow database drivers to implement their own
database-specific SQL rendering methods (e.g., `postgreSQLDescription`)
without duplicating these utility functions.

## Motivation

Previously, boolean values were always encoded as integers, which caused
type mismatch errors when working with PostgreSQL's native BOOLEAN type.
This change allows database drivers to handle booleans appropriately:
- SQLite continues to use integer representation (0/1)
- PostgreSQL can use native BOOLEAN type
- Other databases can implement their preferred boolean handling

## Testing

All existing tests pass. The change is backward compatible and does not
break existing SQLite-based code.

Fixes compatibility issues with PostgreSQL and other databases that have
native boolean types.
@stephencelis
Copy link
Member

@coenttb Thanks! Do you have time to open a SharingGRDB PR against this branch to see what needs to change? Unfortunately these kinds of breaking changes will need to be coordinated.

@coenttb
Copy link
Author

coenttb commented Aug 26, 2025

@stephencelis Done! SharingGRDB PR opened at #141 with minimal changes needed. Just adding the .bool case handling.

@stephencelis
Copy link
Member

Somehow you opened dual 141s 😄 https://github.com/pointfreeco/sharing-grdb/pull/141

Comment on lines +71 to +72
case .bool(let bool):
return bool ? "1" : "0"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for the minor nit, but we like to alphabetize for the most part (invalid is an outlier which is why it comes last).

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I considered that and originally put it there, too. Was thinking it might signal 'this is not an sql type' to put it at the bottom.

@coenttb coenttb closed this Aug 31, 2025
@coenttb coenttb deleted the feature/native-boolean-support branch August 31, 2025 19:03
@stephencelis
Copy link
Member

@coenttb We intended to merge this soon. Any reason why you closed it?

@coenttb
Copy link
Author

coenttb commented Sep 8, 2025

Didn't intentionally do it. But I would also advise against adopting this, and think database adapters through a little more. For my use, I decided to fork the repo and rewrite it for just Postgres. There were more foundational incompatibilities that I came across. I'm a little swamped, but from memory the primary blocking factor was in the Table protocol. Additionally, I had to make some edits in the macros as Postgres handling for nil IDs had to be done a little differently. You can check my progress at swift-structured-queries-postgres.

@stephencelis
Copy link
Member

While we do want to explore that soon, we think the tools are stable enough to cut a 1.0 relatively soon and think these gaps should be handled. We can wait for 2.0 for a better version in the future.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants