{"id":27390156,"url":"https://github.com/ap/fey","last_synced_at":"2025-04-13T19:22:39.314Z","repository":{"id":1620645,"uuid":"2296240","full_name":"ap/Fey","owner":"ap","description":"Better SQL Generation Through Perl","archived":false,"fork":false,"pushed_at":"2025-01-02T22:04:13.000Z","size":3040,"stargazers_count":3,"open_issues_count":0,"forks_count":1,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-04-05T23:16:43.104Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://metacpan.org/release/Fey","language":"Perl","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/ap.png","metadata":{"files":{"readme":"README.md","changelog":"Changes","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","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":"2011-08-30T17:05:52.000Z","updated_at":"2025-01-02T22:04:16.000Z","dependencies_parsed_at":"2025-01-02T20:29:38.937Z","dependency_job_id":"79a02943-4a82-47ab-9b44-2d27e2938d76","html_url":"https://github.com/ap/Fey","commit_stats":null,"previous_names":["autarch-code/fey","ap/fey"],"tags_count":44,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ap%2FFey","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ap%2FFey/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ap%2FFey/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ap%2FFey/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ap","download_url":"https://codeload.github.com/ap/Fey/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248766688,"owners_count":21158302,"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","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":[],"created_at":"2025-04-13T19:22:38.567Z","updated_at":"2025-04-13T19:22:39.304Z","avatar_url":"https://github.com/ap.png","language":"Perl","funding_links":[],"categories":[],"sub_categories":[],"readme":"# NAME\n\nFey - Better SQL Generation Through Perl\n\n# VERSION\n\nversion 0.44\n\n# SYNOPSIS\n\n    use Fey::Literal::Function;\n    use Fey::Placeholder;\n    use Fey::Schema;\n    use Fey::SQL;\n\n\n    my $schema = hand_waving();\n\n    my $user  = $schema-\u003etable('User');\n    my $group = $schema-\u003etable('Group')\n\n    my $select = Fey::SQL-\u003enew_select();\n\n    my $func = Fey::Literal::Function-\u003enew( 'LCASE', $user-\u003ecolumn('username') );\n\n    $select-\u003eselect( $user-\u003ecolumns( 'user_id', 'username' ) )\n           -\u003efrom( $user, $group )\n           -\u003ewhere( $group-\u003egroup_id, 'IN', 1, 2, 3 )\n           -\u003eand  ( $func, 'LIKE', 'smith%' );\n\n    print $select-\u003esql($dbh);\n\n# DESCRIPTION\n\nThe `Fey` distribution contains a set of modules for representing the\ncomponents of a DBMS schema, and for dynamically generating SQL\nqueries based on that schema.\n\n# USAGE\n\nLoading this module does nothing. It's just here to provide docs and a\nversion number for the distro.\n\nYou'll want to take a look at [Fey::Schema](https://metacpan.org/pod/Fey%3A%3ASchema), [Fey::Table](https://metacpan.org/pod/Fey%3A%3ATable), and other\nmodules in the distro for more details.\n\n# WHAT IS Fey?\n\nThe goal of the core `Fey` distro is to provide a simple, flexible\nway of _dynamically_ generating complex SQL queries in Perl. Other\npackages build on top of this functionality to create a complete ORM\n(`Fey::ORM`).\n\n# GETTING STARTED\n\nIf you're interested in an ORM, take a look at the `Fey::ORM` distro.\n\nTo generate SQL with Fey, you first need to create a set of objects\nrepresenting the tables and foreign keys in your schema. The simplest\nway to do this is to use the `Fey-Loader` distro, which will connect\nto an existing schema and generate a set of objects for you.\n\nAlternatively, you can create these objects via Fey's API. You would\nfirst create a [Fey::Schema](https://metacpan.org/pod/Fey%3A%3ASchema) object. This object will hold all of\nyour tables and foreign keys. If you want to create your schema this\nway, you should start with the [Fey::Schema](https://metacpan.org/pod/Fey%3A%3ASchema), [Fey::Table](https://metacpan.org/pod/Fey%3A%3ATable), and\n[Fey::FK](https://metacpan.org/pod/Fey%3A%3AFK) APIs. You'll also want to use the [Fey::Column](https://metacpan.org/pod/Fey%3A%3AColumn) API.\n\nOnce you have a schema, you can generate SQL using [Fey::SQL](https://metacpan.org/pod/Fey%3A%3ASQL), or a\nDBMS-specific subclass of [Fey::SQL](https://metacpan.org/pod/Fey%3A%3ASQL).\n\n# THE CORE Fey DISTRO\n\nThe emphasis in the core Fey distro is on dynamic queries,\nparticularly on the tables/columns/etc involved in the query, not just\nthe bound parameters.\n\nThis is _not_ what I mean by a dynamic query ...\n\n    SELECT user_id FROM User where username = ?\n\nWhile this is dynamic in the sense that the username is parameterized\nand may change on each invocation, it is still easily handled by a\nphrasebook class. If that is all you need, I suggest checking out any\nof `Class::Phrasebook::SQL`, `Data::Phrasebook`, or `SQL::Library`\non CPAN.\n\nImagine that we have a database with a User table and a Message table,\nwhere each message has a user who is that message's creator. We might\nwant to grab all the users in the database, in which case we would do\na simple `SELECT` against the User table ...\n\n    SELECT * FROM User\n\nBut maybe we want to get all the users who have created a message in\nthe last week:\n\n    SELECT User.*\n      FROM User JOIN Message\n           USING (user_id)\n     WHERE Message.creation_date \u003e= ?\n\nThe resultset for our query is still the same (0+ users) but the\nconstraints of the query are more complex. Now imagine another dozen\nor so permutations on how we search for users. This is what I mean by\n\"dynamically\" generating queries.\n\n# RATIONALE\n\nYou probably don't need to read this if you just wanted to know how to\nuse Fey.\n\n## Why Not Use a Phrasebook?\n\nLet's assume we have a simple User table with the following columns:\n\n    username\n    state\n    first_name\n    last_name\n    access_level\n\nLimiting ourselves to queries of equality (\"username = ?\", \"state =\n?\"), we would still need 32 (1 + 5 + 10 + 10 + 5 + 1) entries to\nhandle all the possible combinations of columns. Now imagine adding in\nvariants like allowing for wildcard searches using LIKE or regexes, or\nmore complex variants involving an \"OR\" in a subclause.\n\nThis gets even more complicated if you start adding in joins, outer\njoins, and so on. It's plain to see that a phrasebook gets too large\nto be usable at this point. You'd probably have to write a program\njust to generate the phrasebook and keep it up to date!\n\n## Why Not String Manipulation?\n\nThe next idea that might come to mind is to dump the phrasebook in\nfavor of string manipulation. This is simple enough at first, but\nquickly gets ugly. Handling all of the possible options correctly\nrequires lots of fiddly code that has to concatenate bits of SQL in\nthe correct order, taking into account where to put in commas,\n`WHERE` vs `AND`, and so on and so forth. I've been there, and trust\nme, it's madness.\n\n## The Solution\n\nThe core Fey modules provide a solution to the dynamic SQL\nproblem. Using Fey, you can specify queries in the form of _Perl\nmethods and objects_. Fey provides a set of objects to represent the\nparts of a schema, specifically tables, columns, and foreign\nkeys. Using these objects along with [Fey::SQL](https://metacpan.org/pod/Fey%3A%3ASQL), you can easily\ngenerate very complex queries.\n\nThis core distro is also intended to be the foundation for building\nhigher-level tools like an ORM. See `Fey::ORM` for just such a thing.\n\n# HISTORY AND GOALS\n\nThis module comes from my experience writing and using Alzabo. Alzabo\ndoes everything this module does, and a lot more. The fact that Alzabo\ndoes so many things has become a fairly problematic in its\nmaintenance, and Alzabo was over 6 years old at the time this project\nwas begun (August of 2006).\n\n## Goals\n\nRather than coming up with a very smart solution that allows us to use\n80% of a DBMS's functionality, I'd rather come up with a solution\nthat's dumber but supports all (or at least 99%) of the DBMS's\nfeatures. It's easy to add smarts on top of a dumb layer, but it can\nbe terribly hard to add that last 20% once you've got something really\nsmart.\n\nThe goals for Fey, based on my experience with Alzabo, are the\nfollowing:\n\n- Provide a simple way to generate queries dynamically. I really like\nhow this works with Alzabo conceptually, but Alzabo is not as flexible\nas I'd like and it's \"build a data structure\" approach to query\nbuilding can become very cumbersome.\n\n    Rather than complex data structures, with Fey you call methods on a\n    `Fey::SQL` object to build up a query. This turns out to be simpler\n    to work with.\n\n    Fey, unlike Alzabo, can be used to generate multi-row updates and\n    deletes, and it supports sub-selects, unions, etc. and all that other\n    good stuff.\n\n- Fey supports complex query creation with less fiddliness than\nAlzabo. This means that the class to represent queries is a little\nsmarter and more flexible about the order in which bits are added.\n\n    For example, in using Alzabo I often came across cases where I wanted\n    to add a table to a query's join _if it hasn't already been\n    added_. With Alzabo, there's no nice clean way to do this. Simply\n    adding the table to the join parameter twice will cause an error. It\n    would be nice to simply be able to do this\n\n        $select-\u003ejoin( $foo_table =\u003e $bar_table );\n\n    and have it do the right thing if that join already exists (where the\n    right thing is just do nothing). `Fey::SQL` does exactly that.\n\n- Provide the core for an RDBMS-OO mapper similar to a combination of\n`Alzabo::Runtime::Row` and `Class::AlzaboWrapper`.\n\n    At the same time, query generation and the ORM are decoupled. You can\n    use [Fey::SQL](https://metacpan.org/pod/Fey%3A%3ASQL) to generate queries without having to every use the\n    `Fey::ORM` ORM.\n\n- Be declarative like Moose. In particular, the `Fey::ORM` ORM is as\ndeclarative as possible, and aims to emulate Moose's declarative sugar\nstyle where possible.\n- Leverage the API user's SQL knowledge. Building up queries with Fey\nlooks enough like SQL that you shouldn't have to think _too_ hard\nabout it. This means join support is baked in at a core level, as are\nsubselects and ideally anything else you can do in SQL.\n\n## Problems with Alzabo\n\nHere are some of the problems I've had with Alzabo over the years\nwhich inspired me to create Fey ...\n\n- Adding support for a new DBMS to Alzabo is a lot of work, so it only\nsupports MySQL and Postgres. Alzabo tries to be really smart about\npreventing users from shooting themselves in the foot, and requires a\nlot of DBMS-specific code to achieve this.\n\n    In retrospect, being a lot dumber and allowing for foot-shooting makes\n    supporting a new DBMS much easier. People generally know how their\n    DBMS works, and if they generate an invalid query or table name, it\n    will throw an error.\n\n    For example, while Fey can accommodate per-DBMS query (sub)classes, it does\n    not include any by default, and is capable of supporting many DBMS-specific\n    features without per-DBMS classes.\n\n- Alzabo has too much DBMS-specific knowledge. If you want to use a SQL\nfunction in a query, you have to import a corresponding Perl function\nfrom the appropriate `Alzabo::SQLMaker`, which limits you to what's\nalready defined, or forces you to go through a cumbersome API to\ndefine a new SQL function for use in your Perl code.\n\n    By contrast, Fey has simple generic support for arbitrary functions\n    via the `Fey::Literal::Function` class. If you need more flexibility\n    you can use the `Fey::Literal::Term` subclass to generate an\n    arbitrary snippet to insert into your SQL.\n\n    A related problem is that Alzabo doesn't support multiple versions of\n    a DBMS very well. Either it doesn't work with an older version at all,\n    or it doesn't support some enhanced capability of a newer version. It\n    mostly supports whatever version I happened to be using when I wrote a\n    specific piece of functionality.\n\n- There are now free GUI design tools for specific databases that do a\nbetter job of supporting the database in question than Alzabo ever\nhas.\n- Alzabo separates its classes into Create (for generation of DDL) and\nRuntime (for DML) subclasses, which might have been worth the memory\nsavings six years ago, but just makes for an extra hassle now.\n- When I originally developed Alzabo, I included a feature for\ngenerating high-level application object classes which subclass the\nAlzabo classes and add \"business logic\" methods. This is what is\nprovided by `Alzabo::MethodMaker`.\n\n    Nowadays, I prefer to have my business logic classes simply use the\n    Alzabo classes. In other words, I now prefer \"has-a\" and \"uses-a\"\n    versus \"is-a\" object design for this case.\n\n    Method auto-generation based on a specific schema can be quite handy,\n    but it should be done in the domain-specific application classes, not\n    as a subclass of the core functionality.\n\n- Storing schemas in an Alzabo-specific format is problematic for many\nreasons. It's simpler to simply get the schema definition from an\nexisting schema, or to allow users to define it in code.\n- Alzabo's referential integrity checking code was really cool back when\nI mostly used MySQL with MYISAM tables. Now it's just a maintenance\nburden and a barrier for new features.\n- I didn't catch the testing bug until quite a while after I'd started\nworking on Alzabo. Alzabo's test suite is nasty. Fey is built with\ntestability in mind, and high test coverage is part of my ongoing\ngoals for the project.\n- Alzabo does too many things, which makes it hard to explain and\ndocument.\n\n# WHY IS IT NAMED Fey?\n\nWhen I first started working on Fey, it was named \"Q\". This was a nice\nshort name to type, but obviously unsuitable for releasing on CPAN. I\nwanted a nice short name that could be used in multiple distributions,\nlike John Siracusa's \"Rose\" modules.\n\nI was standing in the shower one day and had the following series of\nthoughts leading to Fey. Reading this will may give you an unpleasant\ninsight into my mind. You have been warned.\n\n- SQLy\n\n    This module is \"SQL-y\", as in \"related to SQL\". However, this name is\n    bad for a number of reasons. First, it's not clear how to pronounce\n    it. It may make you think of a YACC grammar (\"SQL.y\"). It's a weird\n    combo of upper- and lower-case letters.\n\n- SQLy =\u003e Squall\n\n    \"SQLy\" and \"Squall\" share a number of letters, obviously.\n\n    Squall is a single short word, which is good. However, it's a bit\n    awkward to type and has a somewhat negative meaning to me, because a\n    storm can mean trouble.\n\n- Squall =\u003e Lionheart =\u003e Faye\n\n    Squall Lionheart is a character in Final Fantasy VIII, which IMO is\n    the best Final Fantasy game before the PS2.\n\n    The inimitable Faye Wong sang the theme song for FF VIII. I love Faye\n    Wong.\n\n- Faye =\u003e Fey\n\n    And thus we arrive at \"Fey\". It's nice and short, easy to type, and\n    easy to say.\n\n    Some of its meanings are \"otherworldly\" or \"magical\". Attempting to\n    combine SQL and OO in any way is certainly unnatural, and if done\n    right, perhaps magical. Fey can also mean \"appearing slightly\n    crazy\". This project is certainly that.\n\nYes, I'm a nerd, I know.\n\n# BUGS\n\nPlease report any bugs or feature requests to `bug-fey@rt.cpan.org`,\nor through the web interface at [http://rt.cpan.org](http://rt.cpan.org).  I will be\nnotified, and then you'll automatically be notified of progress on\nyour bug as I make changes.\n\nBugs may be submitted at [https://github.com/ap/Fey/issues](https://github.com/ap/Fey/issues).\n\n# SOURCE\n\nThe source code repository for Fey can be found at [https://github.com/ap/Fey](https://github.com/ap/Fey).\n\n# DONATIONS\n\nIf you'd like to thank me for the work I've done on this module, please\nconsider making a \"donation\" to me via PayPal. I spend a lot of free time\ncreating free software, and would appreciate any support you'd care to offer.\n\nPlease note that **I am not suggesting that you must do this** in order for me\nto continue working on this particular software. I will continue to do so,\ninasmuch as I have in the past, for as long as it interests me.\n\nSimilarly, a donation made in this way will probably not make me work on this\nsoftware much more, unless I get so many donations that I can consider working\non free software full time (let's all have a chuckle at that together).\n\nTo donate, log into PayPal and send money to autarch@urth.org, or use the\nbutton at [https://houseabsolute.com/foss-donations/](https://houseabsolute.com/foss-donations/).\n\n# AUTHOR\n\nDave Rolsky \u003cautarch@urth.org\u003e\n\n# CONTRIBUTORS\n\n- Aristotle Pagaltzis \u003cpagaltzis@gmx.de\u003e\n- hdp@glaive.weftsoar.net \u003chdp@glaive.weftsoar.net\u003e\n- hdp@localhost \u0026lt;hdp@localhost\u003e\n- hdp@rook.opensourcery.com \u003chdp@rook.opensourcery.com\u003e\n- Oliver Charles \u003coliver@ocharles.org.uk\u003e\n\n# COPYRIGHT AND LICENSE\n\nThis software is Copyright (c) 2011 - 2025 by Dave Rolsky.\n\nThis is free software, licensed under:\n\n    The Artistic License 2.0 (GPL Compatible)\n\nThe full text of the license can be found in the\n`LICENSE` file included with this distribution.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fap%2Ffey","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fap%2Ffey","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fap%2Ffey/lists"}