{"id":21926994,"url":"https://github.com/rzane/pg_partitions","last_synced_at":"2026-05-10T05:17:16.907Z","repository":{"id":56887923,"uuid":"102225495","full_name":"rzane/pg_partitions","owner":"rzane","description":"ActiveRecord::Migration utility for managing partitions in PostgreSQL","archived":false,"fork":false,"pushed_at":"2017-09-17T23:15:56.000Z","size":18,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-06-14T00:02:51.606Z","etag":null,"topics":["activerecord","migrations","partitioning","postgresql"],"latest_commit_sha":null,"homepage":"","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rzane.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-09-02T21:52:25.000Z","updated_at":"2017-09-17T12:10:56.000Z","dependencies_parsed_at":"2022-08-21T00:50:47.442Z","dependency_job_id":null,"html_url":"https://github.com/rzane/pg_partitions","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/rzane/pg_partitions","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rzane%2Fpg_partitions","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rzane%2Fpg_partitions/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rzane%2Fpg_partitions/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rzane%2Fpg_partitions/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rzane","download_url":"https://codeload.github.com/rzane/pg_partitions/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rzane%2Fpg_partitions/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":266367803,"owners_count":23918649,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-07-21T11:47:31.412Z","response_time":64,"last_error":null,"robots_txt_status":null,"robots_txt_updated_at":null,"robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["activerecord","migrations","partitioning","postgresql"],"created_at":"2024-11-28T22:12:54.792Z","updated_at":"2026-05-10T05:17:16.876Z","avatar_url":"https://github.com/rzane.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"# PgPartitions\n\nPartitioning postgres takes some doing. PgPartitions adds methods to your migrations to help you manage them.\n\n## Installation\n\nAdd this line to your application's Gemfile:\n\n```ruby\ngem 'pg_partitions'\n```\n\nAnd then execute:\n\n    $ bundle\n\n## Usage\n\nImagine you have a comments table with millions of rows and your queries are starting to be a bit slow. Postgres partitioning allows yo to divide your comments table into smaller tables.\n\nIn a migration, you'll first need to include `PgPartitions`.\n\n```ruby\nclass PartitionComments \u003c ActiveRecord::Migration[5.1]\n  include PgPartitions\n\n  def change\n    # ...\n  end\nend\n```\n\nLet's assume we have a column called year that stores the year the comment was created. We can partition our table based on the value of that column:\n\n```ruby\nadd_partition :comments, :comments_2016, check: 'year = 2016'\nadd_partition :comments, :comments_2017, check: 'year = 2017'\n```\n\nAfter we create our partitions, the query plan is going to change a little bit:\n\n```ruby\nComment.all.explain\n=\u003e EXPLAIN for: SELECT \"comments\".* FROM \"comments\"\n                               QUERY PLAN\n------------------------------------------------------------------------\n Append  (cost=0.00..60.80 rows=4081 width=12)\n   -\u003e  Seq Scan on comments  (cost=0.00..0.00 rows=1 width=12)\n   -\u003e  Seq Scan on comments_2016  (cost=0.00..30.40 rows=2040 width=12)\n   -\u003e  Seq Scan on comments_2017  (cost=0.00..30.40 rows=2040 width=12)\n```\n\nSee how it's querying our partitions in addition to the parent table? Now, watch what happens when we put a WHERE condition on the `year` column:\n\n```ruby\nComment.where(year: 2016).explain\n=\u003e EXPLAIN for: SELECT \"comments\".* FROM \"comments\" WHERE \"comments\".\"year\" = $1 [[\"year\", 2016]]\n                              QUERY PLAN\n----------------------------------------------------------------------\n Append  (cost=0.00..35.50 rows=11 width=12)\n   -\u003e  Seq Scan on comments  (cost=0.00..0.00 rows=1 width=12)\n         Filter: (year = 2016)\n   -\u003e  Seq Scan on comments_2016  (cost=0.00..35.50 rows=10 width=12)\n         Filter: (year = 2016)\n```\n\nNotice how it never looked at the `comments_2017` table? That's the magic of partitions.\n\nNow, there's one remaining issue. When we insert data into the `comments` table, we need it to route to be inserted into a partition instead of the actual table. For that, we can create a trigger:\n\n```ruby\nadd_partition_trigger :comments, :comments_by_year, [\n  { if:    'NEW.year = 2016', insert: :comments_2016 },\n  { elsif: 'NEW.year = 2017', insert: :comments_2017 },\n  { else:  \"RAISE EXECEPTION 'comments_by_year recieived an unexpected value: %', NEW.year;\" }\n]\n```\n\nIf the new record has a `year` of 2016, it'll be inserted into the `comments_2016` table. If the `year` is 2017, it'll be inserted into the `comments_2017` table. Otherwise, the trigger will throw an error.\n\nNow, imagine a year goes by and you need to add another partition for `2018`. You'll need to add the partition and update the trigger:\n\n```ruby\nadd_partition :comments, :comments_2018, check: 'NEW.year = 2018'\n\nupdate_partition_trigger :comments, :comments_by_year, [\n  { if:    'NEW.year = 2016', insert: :comments_2016 },\n  { elsif: 'NEW.year = 2017', insert: :comments_2017 },\n  { elsif: 'NEW.year = 2018', insert: :comments_2018 },\n  { else:  \"RAISE EXECEPTION 'comments_by_year recieived an unexpected value: %', NEW.year;\" }\n]\n```\n\n## Caveats\n\n* You'll have to set `config.active_record.schema_format = :sql`. PgPartition doesn't support the use of `schema.rb`.\n\n## Development\n\nAfter checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.\n\nTo install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).\n\n## Contributing\n\nBug reports and pull requests are welcome on GitHub at https://github.com/rzane/pg_partitions.\n\n\n## License\n\nThe gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frzane%2Fpg_partitions","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frzane%2Fpg_partitions","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frzane%2Fpg_partitions/lists"}