diff --git a/kong/dao/cassandra/apis.lua b/kong/dao/cassandra/apis.lua index 573cfa461988..03a3465e12c7 100644 --- a/kong/dao/cassandra/apis.lua +++ b/kong/dao/cassandra/apis.lua @@ -16,32 +16,32 @@ function Apis:new(properties) self._schema = SCHEMA self._queries = { insert = { - params = { "id", "name", "public_dns", "target_url", "created_at" }, + args_keys = { "id", "name", "public_dns", "target_url", "created_at" }, query = [[ INSERT INTO apis(id, name, public_dns, target_url, created_at) VALUES(?, ?, ?, ?, ?); ]] }, update = { - params = { "name", "public_dns", "target_url", "id" }, + args_keys = { "name", "public_dns", "target_url", "id" }, query = [[ UPDATE apis SET name = ?, public_dns = ?, target_url = ? WHERE id = ?; ]] }, select = { query = [[ SELECT * FROM apis %s; ]] }, select_one = { - params = { "id" }, + args_keys = { "id" }, query = [[ SELECT * FROM apis WHERE id = ?; ]] }, delete = { - params = { "id" }, + args_keys = { "id" }, query = [[ DELETE FROM apis WHERE id = ?; ]] }, __unique = { name = { - params = { "name" }, + args_keys = { "name" }, query = [[ SELECT id FROM apis WHERE name = ?; ]] }, public_dns = { - params = { "public_dns" }, + args_keys = { "public_dns" }, query = [[ SELECT id FROM apis WHERE public_dns = ?; ]] } } diff --git a/kong/dao/cassandra/base_dao.lua b/kong/dao/cassandra/base_dao.lua index 53e2e45b264a..12ca97c46993 100644 --- a/kong/dao/cassandra/base_dao.lua +++ b/kong/dao/cassandra/base_dao.lua @@ -1,3 +1,10 @@ +-- Kong's Cassandra base DAO entity. Provides basic functionnalities on top of +-- lua-resty-cassandra (https://github.com/jbochi/lua-resty-cassandra) +-- +-- Entities (APIs, Consumers) having a schema and defined kong_queries can extend +-- this object to benefit from methods such as `insert`, `update`, schema validations +-- (including UNIQUE and FOREIGN check), marshalling of some properties, etc... + local constants = require "kong.constants" local cassandra = require "cassandra" local timestamp = require "kong.tools.timestamp" @@ -9,6 +16,7 @@ local utils = require "kong.tools.utils" local uuid = require "uuid" local rex = require "rex_pcre" +local cassandra_constants = require "cassandra.constants" local error_types = constants.DATABASE_ERROR_TYPES local BaseDao = Object:extend() @@ -21,50 +29,6 @@ function BaseDao:new(properties) self._statements_cache = {} end -------------- --- PRIVATE -- -------------- - -local pattern = "^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$" -local function is_valid_uuid(uuid) - return rex.match(uuid, pattern) ~= nil -end - --- Build the list to pass to lua-resty-cassandra :execute method. --- Since this method only accepts an ordered list, we build this list from --- the `params` property of all prepared statement, taking into account special --- cassandra values (uuid, timestamps, NULL) --- --- @param {table} schema A schema with type properties to encode specific values --- @param {table} t Values to bind to a statement --- @param {table} parameters An ordered list of parameters --- @return {table} An ordered list of values to be binded to lua-resty-cassandra :execute --- @return {table} Error Cassandra type valdiation errors -local function encode_cassandra_values(schema, t, parameters) - local values_to_bind = {} - local errors - for _, column in ipairs(parameters) do - local schema_field = schema[column] - local value = t[column] - - if schema_field.type == constants.DATABASE_TYPES.ID and value then - if is_valid_uuid(value) then - value = cassandra.uuid(value) - else - errors = utils.add_error(errors, column, value.." is an invalid uuid") - end - elseif schema_field.type == constants.DATABASE_TYPES.TIMESTAMP and value then - value = cassandra.timestamp(value) - elseif value == nil then - value = cassandra.null - end - - table.insert(values_to_bind, value) - end - - return values_to_bind, errors -end - -- Marshall an entity. Does nothing by default, -- must be overriden for entities where marshalling applies. function BaseDao:_marshall(t) @@ -77,15 +41,14 @@ function BaseDao:_unmarshall(t) return t end --- Run a statement and check if the result exists --- --- @param {table} t Arguments to bind to the statement --- @param {statement} statement Statement to execute --- @param {boolean} is_updating is_updating If true, will ignore UNIQUE if same entity --- @return {boolean} true if doesn't exist (UNIQUE), false otherwise --- @return {string|nil} Error if any during execution -function BaseDao:_check_unique(statement, t, is_updating) - local results, err = self:_execute(statement, t) +-- Run a statement checking if a row exists (false if it does). +-- @param `kong_query` kong_query to execute +-- @param `t` args to bind to the statement +-- @param `is_updating` If true, will ignore UNIQUE if same entity +-- @return `unique` true if doesn't exist (UNIQUE), false otherwise +-- @return `error` Error if any during execution +function BaseDao:_check_unique(kong_query, t, is_updating) + local results, err = self:_execute_kong_query(kong_query, t) if err then return false, "Error during UNIQUE check: "..err.message elseif results and #results > 0 then @@ -108,15 +71,14 @@ function BaseDao:_check_unique(statement, t, is_updating) end end --- Run a statement and check if the results exists --- --- @param {statement} statement Statement to execute --- @param {table} t Arguments to bind to the statement --- @return {boolean} true if FOREIGN exists, false otherwise --- @return {string|nil} Error if any during execution --- @return {table|nil} Results of the statement if FOREIGN -function BaseDao:_check_foreign(statement, t) - local results, err = self:_execute(statement, t) +-- Run a statement checking if a row exists (true if it does). +-- @param `kong_query` kong_query to execute +-- @param `t` args to bind to the statement +-- @return `exists` true if the row exists (FOREIGN), false otherwise +-- @return `error` Error if any during the query execution +-- @return `results` Results of the statement if `exists` is true (useful for :update() +function BaseDao:_check_foreign(kong_query, t) + local results, err = self:_execute_kong_query(kong_query, t) if err then return false, "Error during FOREIGN check: "..err.message elseif not results or #results == 0 then @@ -126,19 +88,18 @@ function BaseDao:_check_foreign(statement, t) end end --- Run the FOREIGN exists check on all statements in __foreign --- --- @param {table} t Arguments to bind to the __foreign statements --- @return {boolean} true if all results EXIST, false otherwise --- @return {table|nil} Error if any during execution --- @return {table|nil} A table with the list of not existing foreign entities +-- Run the FOREIGN exists check on all statements in __foreign. +-- @param `t` args to bind to the __foreign statements +-- @return `exists` if all results EXIST, false otherwise +-- @return `error` Error if any during the query execution +-- @return `errors` A table with the list of not existing foreign entities function BaseDao:_check_all_foreign(t) if not self._queries.__foreign then return true end local errors - for k, statement in pairs(self._queries.__foreign) do + for k, kong_query in pairs(self._queries.__foreign) do if t[k] and t[k] ~= constants.DATABASE_NULL_ID then - local exists, err = self:_check_foreign(statement, t) + local exists, err = self:_check_foreign(kong_query, t) if err then return false, err elseif not exists then @@ -150,13 +111,12 @@ function BaseDao:_check_all_foreign(t) return errors == nil, nil, errors end --- Run the UNIQUE on all statements in __unique --- --- @param {table} t Arguments to bind to the __unique statements --- @param {boolean} is_updating is_updating If true, will ignore UNIQUE if same entity --- @return {boolean} true if all results are UNIQUE, false otherwise --- @return {table|nil} Error if any during execution --- @return {table|nil} A table with the list of already existing entities +-- Run the UNIQUE on all statements in __unique. +-- @param `t` args to bind to the __unique statements +-- @param `is_updating` If true, will ignore UNIQUE if same entity +-- @return `unique` true if all results are UNIQUE, false otherwise +-- @return `error` Error if any during the query execution +-- @return `errors` A table with the list of already existing entities function BaseDao:_check_all_unique(t, is_updating) if not self._queries.__unique then return true end @@ -177,10 +137,9 @@ function BaseDao:_check_all_unique(t, is_updating) return errors == nil, nil, errors end --- Open a Cassandra session on configured keyspace --- --- @return session --- @return Error if any +-- Open a Cassandra session on the configured keyspace. +-- @return `session` Opened session +-- @return `error` Error if any function BaseDao:_open_session() local ok, err @@ -201,10 +160,9 @@ function BaseDao:_open_session() return session end --- Close the given opened session. Will try to put the session in the socket pool if supported --- --- @param session Cassandra session to close --- @return Error if any +-- Close the given opened session. Will try to put the session in the socket pool if supported. +-- @param `session` Cassandra session to close +-- @return `error` Error if any function BaseDao:_close_session(session) -- Back to the pool or close if using luasocket local ok, err = session:set_keepalive(self._properties.keepalive) @@ -217,57 +175,92 @@ function BaseDao:_close_session(session) end end --- Execute an operation statement. --- The operation can be one of the following: --- * _queries (which contains .query and .param for ordered binding of parameters) and --- will be prepared on the go if not already in the statements cache --- * a lua-resty-cassandra BatchStatement (see ratelimiting_metrics.lua) --- * a lua-resty-cassandra prepared statement --- * a plain query (string) --- --- @param {table} operation The operation to execute --- @param {table} values_to_bind Raw values to bind --- @param {table} options Options to pass to lua-resty-cassandra :execute() --- page_size --- paging_state --- @return {table|boolean} Table if type of return is ROWS --- Boolean if type of results is VOID --- @return {table|nil} Cassandra error if any -function BaseDao:_execute(operation, values_to_bind, options) - local statement = operation +local pattern = "^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$" +local function is_valid_uuid(uuid) + return rex.match(uuid, pattern) ~= nil +end - -- Retrieve the prepared statement from cache or prepare and cache - local cache_key - if operation.query then - cache_key = operation.query - elseif type(operation) == "string" then - cache_key = operation - end +-- Build the array of arguments to pass to lua-resty-cassandra :execute method. +-- Note: +-- Since this method only accepts an ordered list, we build this list from +-- the `args_keys` property of all prepared statement, taking into account special +-- cassandra values (uuid, timestamps, NULL) +-- @param `schema` A schema with type properties to encode specific values +-- @param `t` Values to bind to a statement +-- @param `parameters` An ordered list of parameters +-- @return `args` An ordered list of values to be binded to lua-resty-cassandra :execute +-- @return `error` Error Cassandra type validation errors +local function encode_cassandra_args(schema, t, args_keys) + local args_to_bind = {} + local errors + for _, column in ipairs(args_keys) do + local schema_field = schema[column] + local value = t[column] - if cache_key then - if not self._statements_cache[cache_key] then - statement = self:prepare_kong_statement(cache_key, operation.params) - else - statement = self._statements_cache[cache_key].statement + if schema_field.type == constants.DATABASE_TYPES.ID and value then + if is_valid_uuid(value) then + value = cassandra.uuid(value) + else + errors = utils.add_error(errors, column, value.." is an invalid uuid") + end + elseif schema_field.type == constants.DATABASE_TYPES.TIMESTAMP and value then + value = cassandra.timestamp(value) + elseif value == nil then + value = cassandra.null end + + table.insert(args_to_bind, value) end - -- Bind parameters if operation has some - if operation.params and values_to_bind then - local errors - values_to_bind, errors = encode_cassandra_values(self._schema, values_to_bind, operation.params) - if errors then - return nil, DaoError(errors, error_types.INVALID_TYPE) + return args_to_bind, errors +end + +-- Get a statement from the cache or prepare it (and thus insert it in the cache). +-- The cache key will be the plain string query representation. +-- @param `kong_query` A kong query from the _queries property. +-- @return `statement` The prepared cassandra statement +-- @return `cache_key` The cache key used to store it into the cache +-- @return `error` Error if any during the query preparation +function BaseDao:_get_or_prepare(kong_query) + local query + if type(kong_query) == "string" then + query = kong_query + elseif kong_query.query then + query = kong_query.query + else + -- Cannot be prepared (probably a BatchStatement) + return kong_query + end + + local statement, err + -- Retrieve the prepared statement from cache or prepare and cache + if self._statements_cache[kong_query.query] then + statement = self._statements_cache[kong_query.query].statement + else + statement, err = self:prepare_kong_statement(kong_query) + if err then + return nil, query, err end end - -- Execute operation + return statement, query +end + +-- Execute a statement, BatchStatement or even plain string query. +-- Opens a socket, execute the statement, puts the socket back into the +-- socket pool and returns a parsed result. +-- @param `statement` Prepared statement, plain string query or BatchStatement. +-- @param `args` (Optional) Arguments to the query, simply passed to lua-resty-cassandra's :execute() +-- @param `options` (Optional) Options to give to lua-resty-cassandra's :execute() +-- @return `results` If results set are ROWS, a table with an array of unmarshalled rows and a `next_page` property if the results have a paging_state. +-- @return `error` An error if any during the whole execution (sockets/query execution) +function BaseDao:_execute(statement, args, options) local session, err = self:_open_session() if err then return nil, err end - local results, err = session:execute(statement, values_to_bind, options) + local results, err = session:execute(statement, args, options) if err then err = DaoError(err, error_types.DATABASE) end @@ -301,21 +294,65 @@ function BaseDao:_execute(operation, values_to_bind, options) end end +-- Execute a kong_query (_queries property of DAO entities). +-- Will prepare the query before execution and cache the prepared statement. +-- Will create an arguments array for lua-resty-cassandra's :execute() +-- @param `kong_query` The kong_query to execute +-- @param `args_to_bind` Key/value table of arguments to bind +-- @param `options` Options to pass to lua-resty-cassandra :execute() +-- @return :_execute() +function BaseDao:_execute_kong_query(operation, args_to_bind, options) + -- Prepare query and cache the prepared statement for later call + local statement, cache_key, err = self:_get_or_prepare(operation) + if err then + return nil, err + end + + -- Build args array if operation has some + local args + if operation.args_keys and args_to_bind then + local errors + args, errors = encode_cassandra_args(self._schema, args_to_bind, operation.args_keys) + if errors then + return nil, DaoError(errors, error_types.INVALID_TYPE) + end + end + + -- Execute statement + local results, err + results, err = self:_execute(statement, args, options) + if err and err.cassadra_err_code == cassandra_constants.error_codes.UNPREPARED then + ngx.log(ngx.NOTICE, "Cassandra did not recognize prepared statement \""..cache_key.."\". Re-preparing it and re-trying the query. (Error: "..err..")") + -- If the statement was declared unprepared, clear it from the cache, and try again. + self._statements_cache[cache_key] = nil + return self:_execute_kong_query(operation, args_to_bind, options) + end + + return results, err +end + ---------------------- -- PUBLIC INTERFACE -- ---------------------- --- Prepare a statement used by kong. --- Since lua-resty-cassandra doesn't support binding by name yet, we need --- to keep a record of properties to bind for each statement. Thus, a "kong statement" --- is an object made of a prepared statement and an array of columns to bind. --- See :_execute for the usage of this params array doing the binding. --- --- @param {string} query A CQL query to prepare --- @param {table} params An array of parameters (ordered) matching the query placeholders order --- @return {table|nil} A "kong statement" with a prepared statement and parameters to be used by _execute --- @return {table|nil} Error if any -function BaseDao:prepare_kong_statement(query, params) +-- Prepare a statement used by kong and insert it into the statement cache. +-- Note: +-- Since lua-resty-cassandra doesn't support binding by name yet, we need +-- to keep a record of properties to bind for each statement. Thus, a "kong query" +-- is an object made of a prepared statement and an array of columns to bind. +-- See :_execute_kong_query() for the usage of this args_keys array doing the binding. +-- @param `kong_query` The kong_query to prepare and insert into the cache. +-- @return `statement` The prepared statement, ready to be used by lua-resty-cassandra. +-- @return `error` Error if any during the preparation of the statement +function BaseDao:prepare_kong_statement(kong_query) + -- _queries can contain strings or tables with string + keys of arguments to bind + local query + if type(kong_query) == "string" then + query = kong_query + elseif kong_query.query then + query = kong_query.query + end + -- handle SELECT queries with %s for dynamic select by keys local query_to_prepare = string.format(query, "") query_to_prepare = stringy.strip(query_to_prepare) @@ -335,27 +372,24 @@ function BaseDao:prepare_kong_statement(query, params) if prepare_err then return nil, DaoError("Failed to prepare statement: \""..query_to_prepare.."\". "..prepare_err, error_types.DATABASE) else - local kong_statement = { + -- cache key is the non-striped/non-formatted query from _queries + self._statements_cache[query] = { query = query, - params = params, + args_keys = kong_query.args_keys, statement = prepared_stmt } - -- cache key is the non-striped/non-formatted query from _queries - self._statements_cache[query] = kong_statement - return prepared_stmt end end --- Execute the prepared INSERT statement --- Validate entity's schema + UNIQUE values + FOREIGN KEYS --- Generates id and created_at fields --- --- @param {table} t Entity to insert (binded to statement) --- @return {table|nil} Inserted entity or nil --- @return {table|nil} Error if any +-- Execute the INSERT kong_query of a DAO entity. +-- Validates the entity's schema + UNIQUE values + FOREIGN KEYS. +-- Generates id and created_at fields. +-- @param `t` A table representing the entity to insert +-- @return `result` Inserted entity or nil +-- @return `error` Error if any during the execution function BaseDao:insert(t) local ok, err, errors if not t then @@ -388,7 +422,7 @@ function BaseDao:insert(t) return nil, DaoError(errors, error_types.FOREIGN) end - local _, stmt_err = self:_execute(self._queries.insert, self:_marshall(t)) + local _, stmt_err = self:_execute_kong_query(self._queries.insert, self:_marshall(t)) if stmt_err then return nil, stmt_err else @@ -396,12 +430,11 @@ function BaseDao:insert(t) end end --- Execute the prepared UPDATE statement --- Validate entity's schema + UNIQUE values + FOREIGN KEYS --- --- @param {table} t Entity to insert (binded to statement) --- @return {table|nil} Updated entity or nil --- @return {table|nil} Error if any +-- Execute the UPDATE kong_query of a DAO entity. +-- Validate entity's schema + UNIQUE values + FOREIGN KEYS. +-- @param `t` A table representing the entity to insert +-- @return `result` Updated entity or nil +-- @return `error` Error if any during the execution function BaseDao:update(t) local ok, err, errors if not t then @@ -448,7 +481,7 @@ function BaseDao:update(t) return nil, DaoError(errors, error_types.FOREIGN) end - local _, stmt_err = self:_execute(self._queries.update, self:_marshall(t)) + local _, stmt_err = self:_execute_kong_query(self._queries.update, self:_marshall(t)) if stmt_err then return nil, stmt_err else @@ -456,12 +489,11 @@ function BaseDao:update(t) end end --- Execute the prepared SELECT_ONE statement as it is --- --- @param {string} id UUID of element to select --- @return _execute() +-- Execute the SELECT_ONE kong_query of a DAO entity. +-- @param `id` uuid of the entity to select +-- @return `result` The first row of the _execute_kong_query() return value function BaseDao:find_one(id) - local data, err = self:_execute(self._queries.select_one, { id = id }) + local data, err = self:_execute_kong_query(self._queries.select_one, { id = id }) -- Return the 1st and only element of the result set if data and utils.table_size(data) > 0 then @@ -473,17 +505,12 @@ function BaseDao:find_one(id) return data, err end --- Execute a SELECT statement with special WHERE values --- Build a new prepared statement and cache it for later use --- --- @see _statements_cache --- @warning Generated statement will use ALLOW FILTERING --- --- @param {table} t Optional table from which the WHERE will be built, and the values will be binded --- @param {number} page_size --- @param {paging_state} paging_state --- --- @return _execute() +-- Execute the SELECT kong_query of a DAO entity with a special WHERE clause. +-- @warning Generated statement will use `ALLOW FILTERING` in their queries. +-- @param `t` (Optional) Keys by which to find an entity. +-- @param `page_size` Size of the page to retrieve (number of rows). +-- @param `paging_state` Start page from given offset. See lua-resty-cassandra's :execute() option. +-- @return _execute_kong_query() function BaseDao:find_by_keys(t, page_size, paging_state) local where, keys = {}, {} local where_str = "" @@ -509,26 +536,24 @@ function BaseDao:find_by_keys(t, page_size, paging_state) local select_query = string.format(self._queries.select.query, where_str) - return self:_execute({ query = select_query, params = keys }, t, { + return self:_execute_kong_query({ query = select_query, args_keys = keys }, t, { page_size = page_size, paging_state = paging_state }) end --- Execute the prepared SELECT statement as it is --- --- @param {number} page_size --- @param {paging_state} paging_state +-- Execute the SELECT kong_query of a DAO entity. +-- @param `page_size` Size of the page to retrieve (number of rows). +-- @param `paging_state` Start page from given offset. See lua-resty-cassandra's :execute() option. -- @return find_by_keys() function BaseDao:find(page_size, paging_state) return self:find_by_keys(nil, page_size, paging_state) end --- Execute the prepared DELETE statement --- --- @param {string} id UUID of entity to delete --- @return {boolean} True if deleted, false if otherwise or not found --- @return {table|nil} Error if any +-- Execute the SELECT kong_query of a DAO entity. +-- @param `id` uuid of the entity to delete +-- @return `success` True if deleted, false if otherwise or not found +-- @return `error` Error if any during the query execution function BaseDao:delete(id) local exists, err = self:_check_foreign(self._queries.select_one, { id = id }) if err then @@ -537,7 +562,7 @@ function BaseDao:delete(id) return false end - return self:_execute(self._queries.delete, { id = id }) + return self:_execute_kong_query(self._queries.delete, { id = id }) end return BaseDao diff --git a/kong/dao/cassandra/basicauth_credentials.lua b/kong/dao/cassandra/basicauth_credentials.lua index 0dffbacf6b47..8991f8f3af4c 100644 --- a/kong/dao/cassandra/basicauth_credentials.lua +++ b/kong/dao/cassandra/basicauth_credentials.lua @@ -15,36 +15,36 @@ function BasicAuthCredentials:new(properties) self._schema = SCHEMA self._queries = { insert = { - params = { "id", "consumer_id", "username", "password", "created_at" }, + args_keys = { "id", "consumer_id", "username", "password", "created_at" }, query = [[ INSERT INTO basicauth_credentials(id, consumer_id, username, password, created_at) VALUES(?, ?, ?, ?, ?); ]] }, update = { - params = { "username", "password", "created_at", "id" }, + args_keys = { "username", "password", "created_at", "id" }, query = [[ UPDATE basicauth_credentials SET username = ?, password = ?, created_at = ? WHERE id = ?; ]] }, select = { query = [[ SELECT * FROM basicauth_credentials %s; ]] }, select_one = { - params = { "id" }, + args_keys = { "id" }, query = [[ SELECT * FROM basicauth_credentials WHERE id = ?; ]] }, delete = { - params = { "id" }, + args_keys = { "id" }, query = [[ DELETE FROM basicauth_credentials WHERE id = ?; ]] }, __foreign = { consumer_id = { - params = { "consumer_id" }, + args_keys = { "consumer_id" }, query = [[ SELECT id FROM consumers WHERE id = ?; ]] } }, __unique = { username = { - params = { "username" }, + args_keys = { "username" }, query = [[ SELECT id FROM basicauth_credentials WHERE username = ?; ]] } } diff --git a/kong/dao/cassandra/consumers.lua b/kong/dao/cassandra/consumers.lua index 57044ff6c817..89e615ca5e6a 100644 --- a/kong/dao/cassandra/consumers.lua +++ b/kong/dao/cassandra/consumers.lua @@ -23,31 +23,31 @@ function Consumers:new(properties) self._schema = SCHEMA self._queries = { insert = { - params = { "id", "custom_id", "username", "created_at" }, + args_keys = { "id", "custom_id", "username", "created_at" }, query = [[ INSERT INTO consumers(id, custom_id, username, created_at) VALUES(?, ?, ?, ?); ]] }, update = { - params = { "custom_id", "username", "created_at", "id" }, + args_keys = { "custom_id", "username", "created_at", "id" }, query = [[ UPDATE consumers SET custom_id = ?, username = ?, created_at = ? WHERE id = ?; ]] }, select = { query = [[ SELECT * FROM consumers %s; ]] }, select_one = { - params = { "id" }, + args_keys = { "id" }, query = [[ SELECT * FROM consumers WHERE id = ?; ]] }, delete = { - params = { "id" }, + args_keys = { "id" }, query = [[ DELETE FROM consumers WHERE id = ?; ]] }, __unique = { custom_id ={ - params = { "custom_id" }, + args_keys = { "custom_id" }, query = [[ SELECT id FROM consumers WHERE custom_id = ?; ]] }, username ={ - params = { "username" }, + args_keys = { "username" }, query = [[ SELECT id FROM consumers WHERE username = ?; ]] } } diff --git a/kong/dao/cassandra/factory.lua b/kong/dao/cassandra/factory.lua index 3f9b2f191898..8ccaaefb3504 100644 --- a/kong/dao/cassandra/factory.lua +++ b/kong/dao/cassandra/factory.lua @@ -1,3 +1,9 @@ +-- Kong's cassandra Factory DAO. Entry-point for retrieving DAO objects that allow +-- interations with the database for entities (APIs, Consumers...). +-- +-- Also provides helper methods for preparing queries among the DAOs, migrating the +-- database and dropping it. + local constants = require "kong.constants" local cassandra = require "cassandra" local DaoError = require "kong.dao.error" @@ -65,7 +71,7 @@ end -- in a statements cache -- -- Note: --- Even if the BaseDAO's :_execute() method support preparation of statements on-the-go, +-- Even if the BaseDAO's :_execute_kong_query() method support preparation of statements on-the-go, -- this method should be called when Kong starts in order to detect any failure in advance -- as well as test the connection to Cassandra. -- @@ -78,15 +84,7 @@ function CassandraFactory:prepare() -- Nested queries, let's recurse to prepare them too prepare_collection(collection, collection_query) else - -- _queries can contain strings or tables with string + keys of parameters to bind - local query_to_prepare - if type(collection_query) == "string" then - query_to_prepare = collection_query - elseif collection_query.query then - query_to_prepare = collection_query.query - end - - local _, err = collection:prepare_kong_statement(query_to_prepare, collection_query.params) + local _, err = collection:prepare_kong_statement(collection_query) if err then error(err) end @@ -152,11 +150,11 @@ local MIGRATION_IDENTIFIER = "migrations" -- Create a cassandra session and execute a query on given keyspace or default one (from properties). -- @param query Query or prepared statement given to session:execute --- @param params List of parameters given to session:execute +-- @param args List of arguments given to session:execute -- @param keyspace Optional: overrides properties keyspace if specified -- @return query result -- @return error if any -function CassandraFactory:execute(query, params, keyspace) +function CassandraFactory:execute(query, args, keyspace) local ok, err local session = cassandra.new() session:set_timeout(self._properties.timeout) @@ -171,7 +169,7 @@ function CassandraFactory:execute(query, params, keyspace) return nil, DaoError(err, constants.DATABASE_ERROR_TYPES.DATABASE) end - ok, err = session:execute(query, params) + ok, err = session:execute(query, args) session:close() diff --git a/kong/dao/cassandra/keyauth_credentials.lua b/kong/dao/cassandra/keyauth_credentials.lua index 0b85b84e7056..5f06f5be8361 100644 --- a/kong/dao/cassandra/keyauth_credentials.lua +++ b/kong/dao/cassandra/keyauth_credentials.lua @@ -14,36 +14,36 @@ function KeyAuthCredentials:new(properties) self._schema = SCHEMA self._queries = { insert = { - params = { "id", "consumer_id", "key", "created_at" }, + args_keys = { "id", "consumer_id", "key", "created_at" }, query = [[ INSERT INTO keyauth_credentials(id, consumer_id, key, created_at) VALUES(?, ?, ?, ?); ]] }, update = { - params = { "key", "created_at", "id" }, + args_keys = { "key", "created_at", "id" }, query = [[ UPDATE keyauth_credentials SET key = ?, created_at = ? WHERE id = ?; ]] }, select = { query = [[ SELECT * FROM keyauth_credentials %s; ]] }, select_one = { - params = { "id" }, + args_keys = { "id" }, query = [[ SELECT * FROM keyauth_credentials WHERE id = ?; ]] }, delete = { - params = { "id" }, + args_keys = { "id" }, query = [[ DELETE FROM keyauth_credentials WHERE id = ?; ]] }, __foreign = { consumer_id = { - params = { "consumer_id" }, + args_keys = { "consumer_id" }, query = [[ SELECT id FROM consumers WHERE id = ?; ]] } }, __unique = { key = { - params = { "key" }, + args_keys = { "key" }, query = [[ SELECT id FROM keyauth_credentials WHERE key = ?; ]] } } diff --git a/kong/dao/cassandra/plugins_configurations.lua b/kong/dao/cassandra/plugins_configurations.lua index 3d7f9ed345e5..4b2995040aef 100644 --- a/kong/dao/cassandra/plugins_configurations.lua +++ b/kong/dao/cassandra/plugins_configurations.lua @@ -30,38 +30,38 @@ function PluginsConfigurations:new(properties) self._schema = SCHEMA self._queries = { insert = { - params = { "id", "api_id", "consumer_id", "name", "value", "enabled", "created_at" }, + args_keys = { "id", "api_id", "consumer_id", "name", "value", "enabled", "created_at" }, query = [[ INSERT INTO plugins_configurations(id, api_id, consumer_id, name, value, enabled, created_at) VALUES(?, ?, ?, ?, ?, ?, ?); ]] }, update = { - params = { "api_id", "consumer_id", "value", "enabled", "created_at", "id", "name" }, + args_keys = { "api_id", "consumer_id", "value", "enabled", "created_at", "id", "name" }, query = [[ UPDATE plugins_configurations SET api_id = ?, consumer_id = ?, value = ?, enabled = ?, created_at = ? WHERE id = ? AND name = ?; ]] }, select = { query = [[ SELECT * FROM plugins_configurations %s; ]] }, select_one = { - params = { "id" }, + args_keys = { "id" }, query = [[ SELECT * FROM plugins_configurations WHERE id = ?; ]] }, delete = { - params = { "id" }, + args_keys = { "id" }, query = [[ DELETE FROM plugins_configurations WHERE id = ?; ]] }, __unique = { self = { - params = { "api_id", "consumer_id", "name" }, + args_keys = { "api_id", "consumer_id", "name" }, query = [[ SELECT * FROM plugins_configurations WHERE api_id = ? AND consumer_id = ? AND name = ? ALLOW FILTERING; ]] } }, __foreign = { api_id = { - params = { "api_id" }, + args_keys = { "api_id" }, query = [[ SELECT id FROM apis WHERE id = ?; ]] }, consumer_id = { - params = { "consumer_id" }, + args_keys = { "consumer_id" }, query = [[ SELECT id FROM consumers WHERE id = ?; ]] } } diff --git a/kong/dao/error.lua b/kong/dao/error.lua index d29a109b4bb6..0c543952ec43 100644 --- a/kong/dao/error.lua +++ b/kong/dao/error.lua @@ -43,16 +43,17 @@ local mt = { return nil end - -- Cassandra server error - if err_type == constants.DATABASE_ERROR_TYPES.DATABASE then - err = "Cassandra error: "..err - end - local t = { [err_type] = true, message = err } + -- Cassandra server error + if err_type == constants.DATABASE_ERROR_TYPES.DATABASE then + t.message = "Cassandra error: "..t.message + t.cassadra_err_code = err.code + end + -- If message is a table, use the printable metatable if type(t.message) == "table" then local printable_mt = require "kong.tools.printable"