{"id":13436486,"url":"https://github.com/pllua/pllua","last_synced_at":"2025-10-21T06:23:57.280Z","repository":{"id":65614989,"uuid":"124504418","full_name":"pllua/pllua","owner":"pllua","description":"Re-implementation of pllua, embedded Lua for postgresql","archived":false,"fork":true,"pushed_at":"2023-07-29T19:29:17.000Z","size":838,"stargazers_count":149,"open_issues_count":1,"forks_count":14,"subscribers_count":13,"default_branch":"master","last_synced_at":"2024-10-27T20:19:49.857Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":"RhodiumToad/pllua-ng","license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/pllua.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-03-09T07:28:00.000Z","updated_at":"2024-09-27T22:07:49.000Z","dependencies_parsed_at":"2023-01-31T21:01:25.623Z","dependency_job_id":null,"html_url":"https://github.com/pllua/pllua","commit_stats":null,"previous_names":[],"tags_count":17,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pllua%2Fpllua","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pllua%2Fpllua/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pllua%2Fpllua/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pllua%2Fpllua/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/pllua","download_url":"https://codeload.github.com/pllua/pllua/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244310324,"owners_count":20432514,"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":"2024-07-31T03:00:49.036Z","updated_at":"2025-10-21T06:23:56.945Z","avatar_url":"https://github.com/pllua.png","language":"C","funding_links":[],"categories":["C","Resources"],"sub_categories":["Database drivers"],"readme":"\npllua [![Build Status](https://github.com/pllua/pllua/workflows/Build/badge.svg)](https://github.com/pllua/pllua/actions?query=workflow%3ABuild)\n=====\n\nEmbeds Lua into PostgreSQL as a procedural language module.\n\nPlease report any remaining bugs or missing functionality on github.\n\nCurrently it should build against (recent point releases of) pg\nversions 9.5 to 16. It is known that this module will never work on pg\nversions before 9.5 (we rely critically on memory context callbacks,\nwhich were introduced in that version).\n\nLua 5.4 (only 5.4.2 onwards, excluding 5.4.5), Lua 5.3, and LuaJIT\n2.1beta (with COMPAT52) are fully supported at this time. (Lua 5.4.0\nand 5.4.1 are checked for at runtime and rejected, because they would\notherwise require runtime stack limit calculations; 5.4.5 is checked\nfor at compile and run time and rejected because of an incompatible\nABI/API change.)\n\nDocumentation is being migrated from this README to a more comprehensive\ndocument:\n\n[Documentation](https://pllua.github.io/pllua/)\n\n\nCOMPATIBILITY WITH 0.x (previous pllua-ng releases)\n---------------------------------------------------\n\nThe server.* namespace is removed, to facilitate the compatibility\noption described below.\n\nserver.error, server.debug, etc. are now available via spi.error,\nspi.debug, or can be obtained from the 'pllua.elog' package via\nrequire().  If you want, you can do:\n\n        pllua.on_common_init='server = require \"pllua.elog\"'\n\nto restore the previous behavior.\n\n\nCOMPATIBILITY WITH 1.x (old pllua)\n----------------------------------\n\nThis version permits a degree of compatibility with pllua 1.x via\nthe following setting:\n\n        pllua.on_common_init='require \"pllua.compat\"'\n\nThis creates the server.* namespace with functions that match the 1.x\ncalling conventions (e.g. passing args as tables). It also creates\nglobal functions fromstring(), subtransaction(), debug(), log(),\ninfo(), notice(), warning(), and setshared().\n\nThe following incompatibilities remain:\n\n + coroutine.yield() with no result values in 1.x ended execution of\n   the SRF; in this version it returns a NULL row and continues.\n\n + The global error() is not modified, so using it to throw a table\n   has the same effect it would have in plain Lua. In 1.x, a metatable\n   was added to the thrown value in this case.\n\n + The pgfunc library is not supported at all.\n\n + The \"readonly\" parameter to server.execute and friends is ignored.\n   All queries in a stable function are readonly, and all queries in\n   a volatile function are read-write.\n\n + Returning multi-dimensional arrays by doing a simple return of a\n   Lua table is no longer supported.\n\nThe pllua.compat module is implemented in pure Lua (inside the sandbox\nin trusted mode), see src/compat.lua for the implementation.\n\nPlease report any incompatibilities discovered.\n\n\nCHANGES\n-------\n\n*NOTE:* the name of the module is now just \"pllua\", and its extension\npackaging is split into two according to usual practice for pl modules\n(in this case \"pllua\" for the trusted language and \"plluau\" for the\nuntrusted language).\n\n(Compared to the old pllua project:)\n\nSome names and locations have been changed.\n\nThe old pllua.init table is gone. Instead we support three init\nstrings (superuser-only): pllua.on_init, pllua.on_trusted_init,\npllua.on_untrusted_init.\n\nNote that the on_init string can be run in the postmaster process, by\nincluding pllua in shared_preload_libraries. Accordingly, on_init\ncannot do any database access, and the only function directly\navailable from this module is print(), which in this environment will\noutput to the server log as LOG: messages.\n\nThe on_init string can now access the trusted.allow() functionality,\nbut only by doing an explicit require 'pllua.trusted'. e.g.\n\n      local t = require 'pllua.trusted'\n      t.allow{ \"lpeg\", \"re\" }\n\nSPI functionality is now in global table spi and has different calling\nconventions:\n\n      spi.execute(\"query text\", arg, arg, ...)\n      spi.execute_count(\"query text\", maxrows, arg, arg, ...)\n      spi.prepare(\"query text\", {argtypes}, [{options}])\n        - returns a statement object:\n          s:execute(arg, arg, ...)  - returns a result table\n          s:execute_count(maxrows, arg, arg, ...)  - returns a result table\n          s:rows(arg, arg, ...) - returns iterator\n          s:numargs() - returns integer\n          s:argtype(argnum) - returns typeinfo\n      spi.rows(\"query text\", args...)\n        - returns iterator\n\nExecution now returns a table with no number keys (#t == 0) in the\nevent of no matching rows, whereas the old version returned nil. The\nresult is also currently a plain table, not an object.\n\nspi.prepare takes an options table with these possible values:\n\n      scroll = true or false\n      no_scroll = true\n      fast_start = true\n      custom_plan = true\n      generic_plan = true\n      fetch_count = integer\n\nNote that \"scroll\" and \"no_scroll\" are independent options to the\nplanner, but we treat { scroll = false } as if it were { no_scroll = true }\nbecause not doing so would be too confusing. The fetch_count value is\nused only by the :rows iterator, to determine how much prefetch to use;\nthe default is 50. (Smaller values might be desirable for fetching very\nlarge rows, or a value of 1 disables prefetch entirely.)\n\nCursors work:\n\n      spi.findcursor(\"name\")   - find already-open portal by name\n      spi.newcursor([\"name\"])  - find existing cursor or create new one\n      s:getcursor(args)   - get cursor from statement (can't specify name)\n      c:open(stmt,args)   - open a cursor\n      c:open(query,args)  - open a cursor\n      c:isopen()          - is it open\n      c:name()\n      c:fetch([n, [dir]])  - fetch n rows in dir (default: forward 1)\n      c:move([n, [dir]])\n\nThere can only be one cursor object for a given open portal - doing a\nfindcursor on an existing cursor will always return the same object.\n(But note that this matching is by portal, not name - if a cursor was\nclosed and reopened with the same name, findcursor will return a\ndifferent object for the new cursor.) If a cursor is closed by\nexternal code (or transaction end), then the :isopen() state will be\nautomatically updated (this happens when the portal is actually\ndropped). Cursor options are set on the statement object.\n\nRefcursor parameters and results are transparently converted to and\nfrom SPI cursor objects.\n\n:save on a statement is now a no-op - all statements seen by lua code\nhave been passed through SPI_keepplan and are managed by Lua garbage\ncollection. (It was never safe to do otherwise.)\n\n(SPI interface is particularly subject to change - in particular to\nsomething more compatible with client-side database APIs)\n\nprint() is still a global function to print an informational message,\nbut other error levels such as debug, notice are installed as\nspi.debug(), spi.warning() etc. spi.elog('error', ...) is equivalent\nto spi.error(...) and so on.\n\nspi.error() and friends can take optional args:\n\n      spi.error('message')\n      spi.error('sqlstate', 'message')\n      spi.error('sqlstate', 'message', 'detail')\n      spi.error('sqlstate', 'message', 'detail', 'hint')\n      spi.error({ sqlstate = ?,\n                  message = ?,\n                  detail = ?,\n                  hint = ?,\n                  table = ?,\n                  column = ?, ...})\n\nSqlstates can be given either as 5-character strings or as the string\nnames used in plpgsql: spi.error('invalid_argument', 'message')\n\nSubtransactions are implemented via pcall() and xpcall(), which now\nrun the called function in a subtransaction. In the case of xpcall,\nthe subtransaction is ended *before* running the error function, which\ntherefore runs in the outer subtransaction. This does mean that while\nLua errors in the error function cause recursion and are eventually\ncaught by the xpcall, if an error function causes a PG error then the\nxpcall will eventually rethrow that to its own caller. (This is\nsubject to change if I decide it was a bad idea.)\n\ne.g.\n\n      local ok,err = pcall(function() --[[ do stuff in subxact ]] end)\n      if not ok then print(\"subxact failed with error\",err) end\n\nCurrently there's also an lpcall function which does NOT create\nsubtransactions, but which will catch only Lua errors and not PG\nerrors (which are immediately rethrown). It's not clear yet how useful\nthis is; it saves the (possibly significant) subxact overhead, but it\ncan be quite unpredictable whether any given error will manifest as a\nLua error or a PG error.\n\nThe readonly-global-table and setshared() hacks are omitted. As the\ntrusted language now creates an entirely separate lua_State for each\ncalling userid, anything the user does in the global environment can\nonly affect themselves.\n\nType handling is all different. The global fromstring() is replaced by\nthe pgtype package/function:\n\n      pgtype(d)\n        -- if d is a pg datum value, returns an object representing its\n           type\n\n      pgtype(d,n)\n        -- if d is a datum, as above; if not, returns the object\n           describing the type of argument N of the function, or the\n           return type of the function if N==0\n\n      pgtype['typename']\n      pgtype.typename\n      pgtype(nil, 'typename')\n        -- parse 'typename' as an SQL type and return the object for it\n\n      pgtype.array.typename\n      pgtype.array['typename']\n        -- return the type for \"typename[]\" if it exists\n\nThe object representing a type can then be called as a constructor for\ndatum objects of that type:\n\n      pgtype['mytablename'](col1,col2,col3)\n      pgtype['mytablename']({ col1 = val1, col2 = val2, col3 = val3})\n      pgtype.numeric(1234)\n      pgtype.date('2017-12-01')\n      pgtype.array.integer(1,2,3,4)\n      pgtype.array.integer({1,2,3,4}, 4)        -- dimension mandatory\n      pgtype.array.integer({{1,2},{3,4}},2,2)   -- dimensions mandatory\n      pgtype.numrange(1,2)        -- range type constructor\n      pgtype.numrange(1,2,'[]')   -- range type constructor\n\nor the :fromstring method can be used:\n\n      pgtype.date:fromstring('string')\n\nIn turn, datum objects of composite type can be indexed by column\nnumber or name:\n\n      row.foo  -- value of column \"foo\"\n      row[3]   -- note this is attnum=3, which might not be the third\n                  column if columns have been dropped\n\nArrays can be indexed normally as a[1] or a[3][6] etc. By default\narray indexes in PG start at 1, but values starting at other indexes\ncan be constructed. One-dimensional arrays (but not higher dimensions)\ncan be extended by adding elements with indexes outside the current\nbounds; ranges of unassigned elements between assigned ones contain\nNULL.\n\ntostring() works on any datum and returns its string representation.\n\npairs() works on a composite datum (and actually returns the attnum as a\nthird result):\n\n      for colname,value,attnum in pairs(row) do ...\n\nThe result is always in column order.\n\nipairs() should NOT be used on a composite datum since it will stop at\na null value or dropped column.\n\nArrays, composite types, and jsonb values support a mapping operation\ncontrolled by a configuration table:\n\n      rowval{ map = function(colname,value,attno,row) ... return value end,\n              null = (any value, default nil),\n              discard = (boolean, default false)\n            }\n      arrayval{ map = function(elem,array,i,j,k...) ... return elem end,\n                null = (any value, default nil),\n                discard = (boolean, default false)\n              }\n      jsonbval{ map = function(key,val,...) ... return key,val end,\n                null = (any value, default nil),\n                discard = (boolean, default false),\n                pg_numeric = (boolean, default false)\n              }\n\nThe result in all cases is returned as a Lua table, not a datum,\nunless the \"discard\" option was given as true, in which case no\nresult at all is returned.\n\nThe map function for arrays is passed as many indexes as the original\narray dimension.\n\nThe map function for jsonb values is passed the path leading up to the\ncurrent key (not including the key) as separate additional parameters.\nThe key is an integer if the current container is an array, a string\nif the container is an object, and nil if this is a single top-level\nscalar value (which I believe is not strictly allowed in the json\nspec, but pg allows it). The key/val returned by the function are used\nto store the result, but do not affect the path values passed to any\nother function call. If discard is not specified, then the function is\nalso called for completed containers (in which case val will be a\ntable). If pg_numeric is not true, then numeric values are converted\nto Lua numbers, otherwise they remain as Datum values of numeric type\n(for which see below).\n\nSubstitution of null values happens BEFORE the mapping function is\ncalled; if that's not what you want, then do the substitution yourself\nbefore returning the result. (If the mapping function itself returns a\nLua nil, then the entry will be omitted from the result.)\n\nAs a convenience shorthand, these work:\n\n      d(nvl)   -\u003e d{null = nvl}\n      d(func)  -\u003e d{map = func}\n      d()      -\u003e d{}\n\nJsonb supports an inverse mapping operation for construction of json\nvalues from lua data:\n\n      pgtype.jsonb(value,\n                   { map = function(val) ... return val end,\n                     null = (any value, default nil),\n                     empty_object = (boolean, default false)\n                     array_thresh = (integer, default 1000)\n                     array_frac = (integer, default 1000)\n                   }\n\n\"value\" can be composed of any combination of (where \"collection\"\nmeans a value which is either a table or possesses a __pairs\nmetamethod):\n\n + Empty collections, which will convert to empty json arrays unless\n   empty_object=true in which case they become empty objects\n\n + Collections with only integer keys \u003e= 1, which will convert to json\n   arrays (with lua index 1 becoming json index 0) unless either more\n   than array_thresh initial null values would have to be inserted, or\n   the total size of the array would be more than array_frac times the\n   number of table keys.\n\n + Collections with keys which can be stringified: strings or numbers, or\n   tables or userdata with __tostring methods, will convert to json\n   objects.\n\n + Values which compare raw-equal to the \"null\" parameter are converted\n   to json nulls\n\n + Values of type nil, boolean, number, string are converted to\n   corresponding json values\n\n + Datum values of type pgtype.numeric convert to json numbers\n\n + Datum values of other types convert to json in the same way as they\n   do in SQL; in particular, jsonb and json values are included\n   directly, and values with casts to jsonb have those casts\n   respected\n\n + Values of other types that possess a __tostring metamethod are\n   converted to strings\n\nUnlike the other mapping functions, the map function for this\noperation is called only for values (including collections), not keys,\nand is not passed any path information.\n\nRange types support the following pseudo-columns (immutable):\n\n      r.lower\n      r.upper\n      r.lower_inc\n      r.upper_inc\n      r.lower_inf\n      r.upper_inf\n      r.isempty\n\nFunction arguments are converted to simple Lua values in the case of:\n\n + integers, floats  -- passed as Lua numbers\n\n + text, varchar, char, json (not jsonb), xml, cstring, name -- all passed\n   as strings (with the padding preserved in the case of char(n))\n\n + enums  -- passed as the text label\n\n + bytea  -- passed as a string without any escaping or conversion\n\n + boolean  -- passed as boolean\n \n + nulls of any type  -- passed as nil\n\n + refcursor values are converted to or from SPI cursor objects\n   (whether or not they correspond to open portals)\n\n + domains over any of the above are treated as the base types\n\nOther values are kept as datum objects.\n\nThe trusted language is implemented differently - rather than removing\nfunctions and packages, the trusted language evaluates all\nuser-supplied code (everything but the init strings) in a separate\nenvironment table which contains only whitelisted content. A mini\nversion of the package library is installed in the sandbox\nenvironment, allowing package.preload and package.searchers to work\n(the user can install their own function into package.searchers to\nload modules from database queries if they so wish).\n\nSee the main documentation for details on making additional modules\navailable to the trusted language.\n\nA set-returning function isn't considered to end until it either\nreturns or throws an error; yielding with no results is considered the\nsame as yielding with explicit nils. (Old version killed the thread in\nthat scenario.) A set-returning function that returns on the first\ncall with no result is treated as returning 0 rows, but if the first\ncall returns values, those are treated as the (only) result row.\n\nTrigger functions no longer have a global \"trigger\" object, but rather\nare compiled with the following definition:\n\n      function(trigger,old,new,...) --[[ body here ]] end\n\n\"trigger\" is now a userdata, not a table, but can be indexed as\nbefore.  Trigger functions may assign a row to trigger.row, or modify\nfields of trigger.row or trigger.new, or may return a row or table; if\nthey do none of these and return nothing, they're treated as returning\ntrigger.row unchanged. Note that returning nil or assigning row=nil to\nsuppress the triggered operation is in general a bad idea; if you need\nto prevent an action, then throw an error instead.\n\nAn interface to pg's \"numeric\" type (decimal arithmetic) is provided;\nsee the main documentation for details.\n\nPolymorphic and variadic functions are fully supported, including\nVARIADIC \"any\". VARIADIC of non-\"any\" type is passed as an array as\nusual.\n\nInterpreters are shut down on backend exit, meaning that finalizers\nwill be run for all objects at this time (including user-defined ones).\nCurrently, SPI functionality is disabled during exit.\n\nAUTHOR\n------\n\nAndrew Gierth, aka RhodiumToad\n\nThe author acknowledges the work of Luis Carvalho and other contributors\nto the original pllua project (of which this is a ground-up redesign).\n\nLicense: MIT license\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpllua%2Fpllua","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpllua%2Fpllua","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpllua%2Fpllua/lists"}