{"id":19156881,"url":"https://github.com/franckverrot/holycorn","last_synced_at":"2025-08-23T18:04:52.105Z","repository":{"id":66944612,"uuid":"37829253","full_name":"franckverrot/holycorn","owner":"franckverrot","description":"Community version of the PostgreSQL multi-purpose Ruby Foreign Data Wrapper","archived":false,"fork":false,"pushed_at":"2022-06-09T16:55:28.000Z","size":48,"stargazers_count":165,"open_issues_count":2,"forks_count":9,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-05-12T13:04:51.611Z","etag":null,"topics":["fdw","foreign-data-wrapper","mruby","postgresql","ruby"],"latest_commit_sha":null,"homepage":"","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/franckverrot.png","metadata":{"files":{"readme":"README.md","changelog":"Changelog","contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2015-06-21T22:48:26.000Z","updated_at":"2025-04-23T16:51:19.000Z","dependencies_parsed_at":"2023-09-03T13:21:53.195Z","dependency_job_id":null,"html_url":"https://github.com/franckverrot/holycorn","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/franckverrot/holycorn","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/franckverrot%2Fholycorn","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/franckverrot%2Fholycorn/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/franckverrot%2Fholycorn/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/franckverrot%2Fholycorn/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/franckverrot","download_url":"https://codeload.github.com/franckverrot/holycorn/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/franckverrot%2Fholycorn/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":271760492,"owners_count":24816430,"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-08-23T02:00:09.327Z","response_time":69,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","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":["fdw","foreign-data-wrapper","mruby","postgresql","ruby"],"created_at":"2024-11-09T08:36:16.119Z","updated_at":"2025-08-23T18:04:52.080Z","avatar_url":"https://github.com/franckverrot.png","language":"C","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Holycorn: PostgreSQL multi-purpose Ruby data wrapper [![Travis](https://secure.travis-ci.org/franckverrot/holycorn.png)](http://travis-ci.org/franckverrot/holycorn)\n\n(m)Ruby + PostgreSQL = \u0026lt;3\n\nHolycorn makes it easy to implement a Foreign Data Wrapper using Ruby.\n\nIt is based on top of mruby, that provides sandboxing capabilities the regular\nRuby VM \"MRI/CRuby\" does not implement.\n\n[![Join the chat at https://gitter.im/franckverrot/holycorn](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/franckverrot/holycorn?utm_source=badge\u0026utm_medium=badge\u0026utm_campaign=pr-badge\u0026utm_content=badge)\n\n\n## Built-in Wrappers\n\nHolycorn is the combination of the mruby VM, some supporting gems (some basic\nlibraries and some more advanced ones), and custom code that implement the actual\nforeign data wrappers.\n\nAll the following wrappers are currently linked against Holycorn:\n\n  * `Redis`, using the `mruby-redis` gem\n\n\n## INSTALLATION\n\n### Prerequisites\n\n* PostgreSQL 9.4+\n\n### Setup\n\nSimply run\n\n    rake\n\nto vendor and build `mruby`.\n\nNow that `mruby` is built, building `holycorn` requires to run\n\n    make\n\nand installing it only requires to run\n\n    make install\n\nNow connect to PostgreSQL and install the extension:\n\n    λ psql\n    psql (9.5.14)\n    Type \"help\" for help.\n\n    DROP EXTENSION holycorn CASCADE;\n    CREATE EXTENSION holycorn;\n\n\n### Using Builtin Foreign Data Wrappers\n\n#### Manual Creation of Foreign Tables\n\nA set of builtin FDW are distributed with Holycorn for an easy setup. All one\nneeds to provide are the options that will allow the FDW to be configured:\n\n    λ psql\n    psql (9.5.14)\n    Type \"help\" for help.\n\n    CREATE SERVER holycorn_server FOREIGN DATA WRAPPER holycorn;\n    CREATE FOREIGN TABLE redis_table (key text, value text)\n      SERVER holycorn_server\n      OPTIONS ( wrapper_class 'HolycornRedis'\n              , host '127.0.0.1'\n              , port '6379'\n              , db '0');\n\n\n#### Automatic Import using IMPORT FOREIGN SCHEMA\n\nAlternatively, Holycorn supports `IMPORT FOREIGN SCHEMA` and the same can be\naccomplished by using that statement instead:\n\n    λ psql\n    psql (9.5.14)\n\n    CREATE SERVER holycorn_server FOREIGN DATA WRAPPER holycorn;\n    IMPORT FOREIGN SCHEMA holycorn_schema\n    FROM SERVER holycorn_server\n    INTO holycorn_tables\n    OPTIONS ( wrapper_class 'HolycornRedis'\n            , host '127.0.0.1'\n            , port '6379'\n            , db '0'\n            , prefix 'holycorn_'\n            );\n\nPlease note that custom data wrappers have to use the `holycorn_schema` schema\nas it would otherwise result in an error at runtime.\n\nNote: `IMPORT FOREIGN SCHEMA` requires us to use a custom target schema and\nHolycorn encourages users to use a `prefix` to help avoiding name collisions.\n\n\n#### Access Foreign Tables\n\nAs `Holycorn` doesn't support `INSERT`s yet, let's create some manually:\n\n```console\nλ redis-cli\n127.0.0.1:6379\u003e select 0\nOK\n127.0.0.1:6379\u003e keys *\n(empty list or set)\n127.0.0.1:6379\u003e set foo 1\nOK\n127.0.0.1:6379\u003e set bar 2\nOK\n127.0.0.1:6379\u003e set baz 3\nOK\n127.0.0.1:6379\u003e keys *\n1) \"bar\"\n2) \"foo\"\n3) \"baz\"\n```\n\nNow that the table has been created and we have some data in Redis, we can\nselect data from the foreign table.\n\n```sql\nSELECT * from redis_table;\n\n key | value\n-----+-------\n bar | 2\n foo | 1\n baz | 3\n(3 rows)\n```\n\n#### Using custom scripts\n\nAlternatively, custom scripts can be used as the source for a Foreign Data Wrapper:\n\n```sql\nDROP EXTENSION holycorn CASCADE;\nCREATE EXTENSION holycorn;\nCREATE SERVER holycorn_server FOREIGN DATA WRAPPER holycorn;\nCREATE FOREIGN TABLE holytable (some_date timestamptz) \\\n  SERVER holycorn_server\n  OPTIONS (wrapper_path '/tmp/source.rb');\n```\n\n(`IMPORT FOREIGN SCHEMA` is also supported here.)\n\nAnd the source file of the wrapper:\n\n```ruby\n# /tmp/source.rb\nclass Producer\n  def initialize(env = {}) # env contains informations provided by Holycorn\n  end\n\n  def each\n    @enum ||= Enumerator.new do |y|\n      10.times do |t|\n        y.yield [ Time.now ]\n      end\n    end\n    @enum.next\n  end\n\n  def import_schema(args = {})\n    # Keys are:\n    #   * local_schema: the target schema\n    #   * server_name: name of the foreign data server in-use\n    #   * wrapper_class: name of the current class\n    #   * any other OPTIONS passed to IMPORT FOREIGN SCHEMA\n  end\n\n  self\nend\n```\n\nFor more details about `#import_schema`, please take a look at the examples\nlocated in in the `builtin_wrappers` directory.\n\n\nNow you can select data out of the wrapper:\n\n    λ psql\n    psql (9.5.14)\n    Type \"help\" for help.\n\n    franck=# SELECT * FROM holytable;\n          some_date\n    ---------------------\n     2015-06-21 22:39:24\n     2015-06-21 22:39:24\n     2015-06-21 22:39:24\n     2015-06-21 22:39:24\n     2015-06-21 22:39:24\n     2015-06-21 22:39:24\n     2015-06-21 22:39:24\n     2015-06-21 22:39:24\n     2015-06-21 22:39:24\n     2015-06-21 22:39:24\n    (10 rows)\n\nPretty neat.\n\n# SUPPORTED SCRIPTS\n\n## General rules\n\nAny type of Ruby object can act as a FDW. The only requirements are that it can\nreceive `.new` (with arity = 1) and return an object that can receive `each` (arity = 0).\n\nIt doesn't **have** to be a `Class`, and there's currently no will to provide a\nsuperclass to be inherited from.\n\nIn future versions, there will be many more callbacks to interact with PG's FDW\ninfrastructure through `Holycorn`.\n\nAlso, the script can only be a single word - like `MyClass` - as long as\n`MyClass` has been defined and exists within your compilation of `mruby`.\n\n\n## Environment\n\nA hash is passed by `Holycorn` to the Ruby script. Its current keys are:\n\n* `PG_VERSION`\n* `PG_VERSION_NUM`\n* `PACKAGE_STRING`\n* `PACKAGE_VERSION`\n* `MRUBY_RUBY_VERSION`\n* `WRAPPER_PATH`\n\n\n# SUPPORTED TYPES (Ruby =\u003e PG)\n\n## Builtin types\n\n  * `MRB_TT_FREE`      =\u003e `null`\n  * `MRB_TT_FALSE`     =\u003e `Boolean`\n  * `MRB_TT_TRUE`      =\u003e `Boolean`\n  * `MRB_TT_FIXNUM`    =\u003e `Int64`\n  * `MRB_TT_SYMBOL`    =\u003e `Text`\n  * `MRB_TT_UNDEF`     =\u003e Unsupported\n  * `MRB_TT_FLOAT`     =\u003e `Float8`\n  * `MRB_TT_CPTR`      =\u003e Unsupported\n  * `MRB_TT_OBJECT`    =\u003e `Text` (`to_s` is called)\n  * `MRB_TT_CLASS`     =\u003e `Text` (`class.to_s` is called)\n  * `MRB_TT_MODULE`    =\u003e `Text` (`to_s` is called)\n  * `MRB_TT_ICLASS`    =\u003e Unsupported\n  * `MRB_TT_SCLASS`    =\u003e Unsupported\n  * `MRB_TT_PROC`      =\u003e `Text` (`inspect` is called)\n  * `MRB_TT_ARRAY`     =\u003e `Text` (`inspect` is called)\n  * `MRB_TT_HASH`      =\u003e `Text` (`inspect` is called)\n  * `MRB_TT_STRING`    =\u003e `Text`\n  * `MRB_TT_RANGE`     =\u003e `Text` (`inspect` is called)\n  * `MRB_TT_EXCEPTION` =\u003e Unsupported\n  * `MRB_TT_FILE`      =\u003e Unsupported\n  * `MRB_TT_ENV`       =\u003e Unsupported\n  * `MRB_TT_DATA`      =\u003e See \"Arbitraty Ruby objects\" section\n  * `MRB_TT_FIBER`     =\u003e `Text` (`inspect` is called)\n  * `MRB_TT_MAXDEFINE` =\u003e Unsupported\n\n## Arbitraty Ruby objects\n\n  * Time (Ruby) =\u003e `timestamptz`\n\n## CONFIGURATION\n\n### Server\n\nNone (yet).\n\n### Foreign Table\n\nEither `wrapper_class` or `wrapper_path` can be used to defined whether an\nexternal script or a built-in wrapper will manage the foreign table.\n\n* `wrapper_class`: Name of the built-in wrapper class\n* `wrapper_path`: Path of a custom script\n\nIn both case, any other option will be pushed down to the wrapper class via the\nconstructor.\n\n\n## TODO\n\n- [ ] Array type\n- [ ] JSON type\n- [ ] Range type\n- [ ] Support PG 9.5's `IMPORT FOREIGN SCHEMA` for easy setup\n\n## Note on Patches/Pull Requests\n\n* Fork the project.\n* Make your feature addition or bug fix.\n* Add tests for it. This is important so I don't break it in a future version unintentionally.\n* Commit, do not mess with version or history. (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)\n* Send me a pull request. Bonus points for topic branches.\n\n## LICENSE\n\nCopyright (c) 2015-2018 Franck Verrot\n\nHolycorn is an Open Source project licensed under the terms of the LGPLv3\nlicense.  Please see \u003chttp://www.gnu.org/licenses/lgpl-3.0.html\u003e for license\ntext.\n\n## AUTHOR\n\nFranck Verrot, @franckverrot\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffranckverrot%2Fholycorn","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffranckverrot%2Fholycorn","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffranckverrot%2Fholycorn/lists"}