%% -*- mode:erlang, erlang-indent-level: 4;indent-tabs-mode: nil -*-
%% Copyright 2012 Opscode, Inc. All Rights Reserved.
%%
%% This file is provided to you under the Apache License,
%% Version 2.0 (the "License"); you may not use this file
%% except in compliance with the License.  You may obtain
%% a copy of the License at
%%
%%   http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing,
%% software distributed under the License is distributed on an
%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
%% KIND, either express or implied.  See the License for the
%% specific language governing permissions and limitations
%% under the License.
%%


{ping, <<"SELECT 'pong' as ping LIMIT 1">>}.

%% chef user queries
%% open source users have all the data of a private chef user + the admin field

%% id is a guid - for OSC it is currently the couchid - will need to generate it in code
%% authz_id - will need to generate fake authz_id to meet requirements and fill that in
%% email can be null
%% pubkey_version is currently not used by osc users, so set to 1 as default, indicating a CERT
%% public_key can be null, but it might be best to generate a user a key and put it in here
%% serialized object is where the password and salt are stored
%% last_updated_by, created_at, and updated_at are requied
%% external_authentication_uid is optional, but is where the open_id will be stored.
%% recovery_authentication_enable is optional and not used
%% admin is required and used by osc user, set to false by default by the db
{insert_user,
  <<"INSERT INTO osc_users (id, authz_id, username, email, public_key,
      hashed_password, salt, hash_type, last_updated_by, created_at,
      updated_at, external_authentication_uid,
      recovery_authentication_enabled, admin) VALUES
      ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14)">>}.

{find_user_by_username,
  <<"SELECT id, authz_id, username, email, public_key,
      hashed_password, salt, hash_type, last_updated_by, created_at,
      updated_at, external_authentication_uid,
      recovery_authentication_enabled, admin
      FROM osc_users WHERE username = $1">>}.

{delete_user_by_username,
  <<"DELETE FROM osc_users WHERE username = $1">>}.

{list_users, <<"SELECT username FROM osc_users">>}.

%% Query to count the number of admins, so the last one isn't deleted
{count_user_admins, <<"SELECT COUNT(*) as count FROM osc_users WHERE admin = true">>}.

%% update query need to go here
{update_user_by_id,
<<"UPDATE osc_users
     SET admin = $1,
         public_key = $2,
         hashed_password = $3,
         salt = $4,
         hash_type = $5,
         last_updated_by = $6,
         updated_at = $7"
  "  WHERE id = $8">>
}.

%% node queries

{find_node_by_orgid_name,
 <<"SELECT id, authz_id, org_id, name, environment, last_updated_by,"
   " created_at, updated_at, serialized_object FROM nodes WHERE (org_id = $1"
   " AND name = $2) LIMIT 1">>}.

{insert_node,
 <<"INSERT INTO nodes (id, authz_id, org_id, name, environment,"
   " last_updated_by, created_at, updated_at, serialized_object ) VALUES"
   " ($1, $2, $3, $4, $5, $6, $7, $8, $9)">>}.

{update_node_by_id,
 <<"UPDATE nodes SET environment= $1, last_updated_by= $2, updated_at= $3, "
   "serialized_object= $4 WHERE id= $5">>}.

{delete_node_by_id, <<"DELETE FROM nodes WHERE id= $1">>}.

{list_nodes_for_org, <<"SELECT name FROM nodes WHERE org_id= $1">>}.
{list_env_nodes_for_org,
 <<"SELECT name FROM nodes WHERE org_id= $1 AND environment = $2">>}.

{list_node_ids_names_for_org,
 <<"SELECT id, name FROM nodes WHERE org_id = $1">>}.

%% bulk_get_nodes_X queries used for fetching nodes returned from
%% search only returns the gzip JSON data.  If a node has been
%% deleted, you may get back fewer than X nodes and you won't know
%% which ids were not found without parsing and inspecting what did
%% come back.
{bulk_get_nodes_1, <<"SELECT serialized_object FROM nodes WHERE id IN ($1)">>}.
{bulk_get_nodes_2, <<"SELECT serialized_object FROM nodes WHERE id IN ($1,$2)">>}.
{bulk_get_nodes_3, <<"SELECT serialized_object FROM nodes WHERE id IN ($1,$2,$3)">>}.
{bulk_get_nodes_4, <<"SELECT serialized_object FROM nodes WHERE id IN ($1,$2,$3,$4)">>}.
{bulk_get_nodes_5, <<"SELECT serialized_object FROM nodes WHERE id IN ($1,$2,$3,$4,$5)">>}.

%% role queries

{find_role_by_orgid_name,
 <<"SELECT id, authz_id, org_id, name, last_updated_by,"
   " created_at, updated_at, serialized_object FROM roles WHERE (org_id = $1"
   " AND name = $2) LIMIT 1">>}.

{insert_role,
 <<"INSERT INTO roles (id, authz_id, org_id, name,"
   " last_updated_by, created_at, updated_at, serialized_object ) VALUES"
   " ($1, $2, $3, $4, $5, $6, $7, $8)">>}.

{update_role_by_id,
 <<"UPDATE roles SET last_updated_by= $1, updated_at= $2, "
   "serialized_object= $3 WHERE id= $4">>}.

{delete_role_by_id, <<"DELETE FROM roles WHERE id= $1">>}.

{list_roles_for_org, <<"SELECT name FROM roles WHERE org_id= $1">>}.

{list_role_ids_names_for_org,
 <<"SELECT id, name FROM roles WHERE org_id = $1">>}.

%% bulk_get_roles_X queries used for fetching roles returned from
%% search only returns the gzip JSON data.  If a role has been
%% deleted, you may get back fewer than X roles and you won't know
%% which ids were not found without parsing and inspecting what did
%% come back.
{bulk_get_roles_1, <<"SELECT serialized_object FROM roles WHERE id IN ($1)">>}.
{bulk_get_roles_2, <<"SELECT serialized_object FROM roles WHERE id IN ($1,$2)">>}.
{bulk_get_roles_3, <<"SELECT serialized_object FROM roles WHERE id IN ($1,$2,$3)">>}.
{bulk_get_roles_4, <<"SELECT serialized_object FROM roles WHERE id IN ($1,$2,$3,$4)">>}.
{bulk_get_roles_5, <<"SELECT serialized_object FROM roles WHERE id IN ($1,$2,$3,$4,$5)">>}.

%% data_bag queries

{find_data_bag_by_orgid_name,
 <<"SELECT id, authz_id, org_id, name, last_updated_by,"
   " created_at, updated_at FROM data_bags WHERE (org_id = $1"
   " AND name = $2) LIMIT 1">>}.

{insert_data_bag,
 <<"INSERT INTO data_bags (id, authz_id, org_id, name, "
   " last_updated_by, created_at, updated_at) VALUES"
   " ($1, $2, $3, $4, $5, $6, $7)">>}.

{update_data_bag_by_id,
 <<"UPDATE data_bags SET last_updated_by= $1, updated_at= $2 WHERE id= $3">>}.

{delete_data_bag_by_id, <<"DELETE FROM data_bags WHERE id= $1">>}.

{list_data_bags_for_org, <<"SELECT name FROM data_bags WHERE org_id= $1">>}.

%% We don't bulk_get data_bags, but only data_bag_items.

%% data_bag_item queries

{find_data_bag_item_by_orgid_name,
 <<"SELECT id, org_id, data_bag_name, item_name, last_updated_by,"
   " created_at, updated_at, serialized_object FROM data_bag_items WHERE (org_id = $1"
   " AND data_bag_name = $2 AND item_name = $3) LIMIT 1">>}.

{find_data_bag_item_id_by_orgid_name,
 <<"SELECT id FROM data_bag_items WHERE "
   "(org_id = $1 AND data_bag_name = $2)">>}.

{insert_data_bag_item,
 <<"INSERT INTO data_bag_items (id,org_id, data_bag_name, item_name,"
   " last_updated_by, created_at, updated_at, serialized_object ) VALUES"
   " ($1, $2, $3, $4, $5, $6, $7, $8)">>}.

{update_data_bag_item_by_id,
 <<"UPDATE data_bag_items SET last_updated_by= $1, updated_at= $2, "
   "serialized_object= $3 WHERE id= $4">>}.

{delete_data_bag_item_by_id, <<"DELETE FROM data_bag_items WHERE id= $1">>}.

{list_data_bag_items_for_data_bag, <<"SELECT item_name FROM data_bag_items "
                                     "WHERE (org_id= $1 AND data_bag_name= $2)">>}.

{list_data_bag_item_ids_names_for_org,
 <<"SELECT id, item_name FROM data_bag_items WHERE org_id = $1 AND data_bag_name = $2">>}.

%% bulk_get_data_bag_items_X queries used for fetching data_bag_items returned from
%% search only returns the gzip JSON data.  If a data_bag_item has been
%% deleted, you may get back fewer than X data_bag_items and you won't know
%% which ids were not found without parsing and inspecting what did
%% come back.
{bulk_get_data_bag_items_1, <<"SELECT serialized_object FROM data_bag_items WHERE id IN ($1)">>}.
{bulk_get_data_bag_items_2, <<"SELECT serialized_object FROM data_bag_items WHERE id IN ($1,$2)">>}.
{bulk_get_data_bag_items_3, <<"SELECT serialized_object FROM data_bag_items WHERE id IN ($1,$2,$3)">>}.
{bulk_get_data_bag_items_4, <<"SELECT serialized_object FROM data_bag_items WHERE id IN ($1,$2,$3,$4)">>}.
{bulk_get_data_bag_items_5, <<"SELECT serialized_object FROM data_bag_items WHERE id IN ($1,$2,$3,$4,$5)">>}.

%% environment queries

{find_environment_by_orgid_name,
 <<"SELECT id, authz_id, org_id, name, last_updated_by,"
   " created_at, updated_at, serialized_object FROM environments WHERE (org_id = $1"
   " AND name = $2) LIMIT 1">>}.

{insert_environment,
 <<"INSERT INTO environments (id, authz_id, org_id, name,"
   " last_updated_by, created_at, updated_at, serialized_object ) VALUES"
   " ($1, $2, $3, $4, $5, $6, $7, $8)">>}.

{update_environment_by_id,
 <<"UPDATE environments SET last_updated_by= $1, updated_at= $2, "
   "name= $3, serialized_object= $4 WHERE id= $5">>}.

{delete_environment_by_id, <<"DELETE FROM environments WHERE id= $1">>}.

{list_environments_for_org, <<"SELECT name FROM environments WHERE org_id= $1">>}.

{list_environment_ids_names_for_org,
 <<"SELECT id, name FROM environments WHERE org_id = $1">>}.

%% bulk_get_environments_X queries used for fetching environments returned from
%% search only returns the gzip JSON data.  If a environment has been
%% deleted, you may get back fewer than X environments and you won't know
%% which ids were not found without parsing and inspecting what did
%% come back.
{bulk_get_environments_1, <<"SELECT serialized_object FROM environments WHERE id IN ($1)">>}.
{bulk_get_environments_2, <<"SELECT serialized_object FROM environments WHERE id IN ($1,$2)">>}.
{bulk_get_environments_3, <<"SELECT serialized_object FROM environments WHERE id IN ($1,$2,$3)">>}.
{bulk_get_environments_4, <<"SELECT serialized_object FROM environments WHERE id IN ($1,$2,$3,$4)">>}.
{bulk_get_environments_5, <<"SELECT serialized_object FROM environments WHERE id IN ($1,$2,$3,$4,$5)">>}.

%% client queries

{find_client_by_orgid_name,
 <<"SELECT id, authz_id, org_id, name, validator, admin, public_key, pubkey_version,"
   "    last_updated_by, created_at, updated_at"
   "  FROM clients"
   "  WHERE (org_id = $1 AND name = $2) LIMIT 1">>}.

{insert_client,
 <<"INSERT INTO clients ( id, authz_id, org_id, name, validator, admin, public_key,"
   "    pubkey_version, last_updated_by, created_at, updated_at )"
   "  VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)">>}.

{update_client_by_id,
 <<"UPDATE clients SET last_updated_by = $1, updated_at = $2, "
   "name = $3, public_key = $4, pubkey_version = $5, "
   "validator = $6, admin = $7  WHERE id = $8">>}.

{delete_client_by_id, <<"DELETE FROM clients WHERE id= $1">>}.

{list_clients_for_org, <<"SELECT name FROM clients WHERE org_id= $1">>}.

{list_client_ids_names_for_org,
 <<"SELECT id, name FROM clients WHERE org_id = $1">>}.

%% bulk_get_clients_X queries used for fetching clients returned from
%% search returns the minimum fields needed to construct a client
%% search response since there is no serialized object.  Note for compatibility
%% search on clientname in ruby we return the name twice, once as 'name' and
%% once as 'clientname'
%%
%% If a client has been deleted, you may get back fewer than X clients
%% and you won't know which ids were not found without parsing and
%% inspecting what did come back.
{bulk_get_clients_1,
 <<"SELECT id, authz_id, org_id, name, validator, admin, public_key, pubkey_version,"
   "    last_updated_by, created_at, updated_at"
   "  FROM clients WHERE id IN ($1)">>}.
{bulk_get_clients_2,
 <<"SELECT id, authz_id, org_id, name, validator, admin, public_key, pubkey_version,"
   "    last_updated_by, created_at, updated_at"
   "  FROM clients WHERE id IN ($1,$2)">>}.
{bulk_get_clients_3,
 <<"SELECT id, authz_id, org_id, name, validator, admin, public_key, pubkey_version,"
   "    last_updated_by, created_at, updated_at"
   "  FROM clients WHERE id IN ($1,$2,$3)">>}.
{bulk_get_clients_4,
 <<"SELECT id, authz_id, org_id, name, validator, admin, public_key, pubkey_version,"
   "    last_updated_by, created_at, updated_at"
   "  FROM clients WHERE id IN ($1,$2,$3,$4)">>}.
{bulk_get_clients_5,
 <<"SELECT id, authz_id, org_id, name, validator, admin, public_key, pubkey_version,"
   "    last_updated_by, created_at, updated_at"
   "  FROM clients WHERE id IN ($1,$2,$3,$4,$5)">>}.

%% cookbook queries
{find_cookbook_by_orgid_name,
 <<"SELECT authz_id, org_id, name FROM cookbooks WHERE (org_id = $1 AND name = $2) LIMIT 1">>}.

{insert_cookbook,
 <<"INSERT INTO cookbooks (authz_id, org_id, name) VALUES ($1, $2, $3)">>}.

{delete_cookbook_by_orgid_name, <<"DELETE FROM cookbooks WHERE (org_id = $1 AND name = $2)">>}.

%% cookbook version queries
{find_cookbook_version_by_orgid_name_version,
 <<"SELECT v.id,  v.major, v.minor, v.patch, v.frozen, v.meta_attributes,
    v.meta_deps, v.meta_long_desc, v.metadata, v.serialized_object, v.last_updated_by,
    v.created_at, v.updated_at, c.authz_id, c.org_id, c.name FROM cookbooks AS c, cookbook_versions AS v
    WHERE (v.cookbook_id = c.id AND c.org_id = $1 AND c.name = $2
    AND v.major = $3 AND v.minor = $4 AND v.patch = $5) LIMIT 1">>}.

{find_latest_cookbook_version_by_orgid_name,
<<"SELECT v.id, v.major, v.minor, v.patch, v.frozen, v.meta_attributes,
   v.meta_deps, v.meta_long_desc, v.metadata, v.serialized_object, v.last_updated_by,
   v.created_at, v.updated_at, c.authz_id, c.org_id, c.name FROM cookbooks AS c, cookbook_versions  AS v
   WHERE (v.cookbook_id = c.id AND c.org_id = $1 AND c.name = $2)
   ORDER BY v.major DESC, v.minor DESC, v.patch DESC LIMIT 1">>}.


{insert_cookbook_version,
 <<"INSERT INTO cookbook_versions (id, major, minor, patch, frozen, "
   "meta_attributes, meta_deps, meta_long_desc, metadata, serialized_object, "
   "last_updated_by, created_at, updated_at, cookbook_id) VALUES ($1, $2, $3, $4, "
   "$5, $6, $7, $8, $9, $10, $11, $12, $13, "
   "(SELECT id FROM cookbooks WHERE authz_id = $14 AND org_id = $15 AND name = $16))">>}.

{update_cookbook_version,
 <<"UPDATE cookbook_versions"
   " SET frozen = $1, meta_attributes = $2, meta_deps = $3, meta_long_desc = $4,"
   "   metadata = $5, serialized_object = $6, last_updated_by = $7, updated_at = $8"
   " WHERE id = $9">>}.

{delete_cookbook_version_by_id, <<"DELETE FROM cookbook_versions WHERE id = $1">>}.

{insert_cookbook_version_checksum,
 <<"INSERT INTO cookbook_version_checksums (checksum, org_id, cookbook_version_id) "
   "VALUES ($1, $2, (SELECT cookbook_versions.id FROM cookbooks, cookbook_versions "
   "                 WHERE cookbooks.id = cookbook_versions.cookbook_id AND "
   "                       cookbooks.name = $3 AND "
   "                       cookbooks.org_id = $4 AND "
   "                       major = $5 AND minor = $6 AND patch = $7))">>}.

{delete_cookbook_version_checksum, <<"DELETE FROM cookbook_version_checksums WHERE checksum = $1 AND org_id = $2 AND cookbook_version_id = $3">>}.

{list_cookbook_versions_by_orgid,
  <<"SELECT cookbooks.name, major, minor, patch FROM cookbooks, cookbook_versions "
    "WHERE (org_id = $1 AND cookbooks.id = cookbook_versions.cookbook_id) "
    "ORDER BY name, major DESC, minor DESC, patch DESC">>}.

{list_cookbook_versions_by_orgid_cookbook_name,
  <<"SELECT cookbooks.name, major, minor, patch FROM cookbooks, cookbook_versions "
    "WHERE (org_id = $1 AND cookbooks.name = $2 AND cookbooks.id = cookbook_versions.cookbook_id) "
    "ORDER BY name, major DESC, minor DESC, patch DESC">>}.

%% 'serialized_object' contains the recipes
{fetch_latest_cookbook_recipes_by_orgid,
 <<"SELECT name, serialized_object
    FROM cookbook_versions_by_rank
    WHERE org_id = $1
      AND rank = 1
    ORDER BY name ASC">>}.

{fetch_recent_cookbook_versions_by_orgid_and_num,
 <<"SELECT name, version
   FROM cookbook_versions_by_rank
   WHERE org_id = $1
     AND rank <= $2
   ORDER BY name ASC, major DESC, minor DESC, patch DESC">>}.

%% Though this query could utilize the `cookbook_versions_by_rank'
%% view, we use a slightly different view here that does not use the
%% `rank' window function.  We don't actually need
%% `rank' here, so we shouldn't need to compute it.
{fetch_all_cookbook_versions_by_orgid,
 <<"SELECT name, version
   FROM joined_cookbook_version
   WHERE org_id = $1
   ORDER BY name ASC, major DESC, minor DESC, patch DESC">>}.

%% Used for getting all the versions of a cookbook, as filtered
%% through an environment's constraints
{fetch_all_cookbook_versions_by_orgid_cookbook,
 <<"SELECT name, version
   FROM joined_cookbook_version
   WHERE org_id = $1
     AND name = $2
   ORDER BY major DESC, minor DESC, patch DESC">>}.

%% Used in implementation of environments/ENVIRONMENT/recipes endpoint.
%%
%% We do NOT require these results to be ordered, as the results are
%% being dumped into a dict anyway.
%%
%% We do not use cookbook_versions_by_rank for this, because
%% ranking is unnecessary.
{fetch_all_cookbook_version_ids_by_orgid,
 <<"SELECT name, version, id
    FROM joined_cookbook_version
    WHERE org_id = $1">>}.

%% Used in implementation of environments/ENVIRONMENT/recipes endpoint
%%
%% These queries shouldn't use the `cookbook_versions_by_rank' view,
%% even though the information is present and we wouldn't need the
%% JOIN clause, because it will compute ranks unnecessarily; we are
%% just interested in individual cookbook versions here.
{bulk_get_cbv_serialized_object_1,
 <<"SELECT name, serialized_object
    FROM joined_cookbook_version
    WHERE id IN ($1)">>}.
{bulk_get_cbv_serialized_object_2,
 <<"SELECT name, serialized_object
    FROM joined_cookbook_version
    WHERE id IN ($1, $2)">>}.
{bulk_get_cbv_serialized_object_3,
 <<"SELECT name, serialized_object
    FROM joined_cookbook_version
    WHERE id IN ($1, $2, $3)">>}.
{bulk_get_cbv_serialized_object_4,
 <<"SELECT name, serialized_object
    FROM joined_cookbook_version
    WHERE id IN ($1, $2, $3, $4)">>}.
{bulk_get_cbv_serialized_object_5,
 <<"SELECT name, serialized_object
    FROM joined_cookbook_version
    WHERE id IN ($1, $2, $3, $4, $5)">>}.


{fetch_all_cookbook_version_dependencies_by_orgid,
 <<"SELECT name, major || '.' || minor || '.' || patch AS version, dependencies
    FROM cookbook_version_dependencies
    WHERE org_id = $1
    ORDER BY name, major DESC, minor DESC, patch DESC">>}.

{list_cookbook_checksums_by_orgid_cookbook_versionid,
  <<"SELECT checksum FROM cookbook_versions, cookbook_version_checksums "
    "WHERE (cookbook_version_checksums.org_id = $1 AND cookbook_versions.id = $2) "
    "AND (cookbook_version_checksums.cookbook_version_id = cookbook_versions.id)">>}.

{delete_cookbook_checksums_by_orgid_cookbook_versionid,
  <<"DELETE FROM cookbook_version_checksums "
    "WHERE org_id = $1 AND cookbook_version_id = $2">>}.

%% Sandbox Queries
{find_sandbox_by_id,
<<"SELECT sc.org_id, sc.sandbox_id, sc.checksum, c.checksum IS NOT NULL AS uploaded, sc.created_at
   FROM sandboxed_checksums AS sc
   LEFT JOIN checksums AS c
          ON sc.org_id = c.org_id
         AND sc.checksum = c.checksum
   WHERE sc.org_id = $1
     AND sc.sandbox_id = $2">>}.

{delete_sandbox_by_id,
 <<"DELETE FROM sandboxed_checksums WHERE sandbox_id = $1">>}.

{insert_checksum,
  <<"INSERT INTO checksums(org_id, checksum)
     VALUES ($1, $2)">>}.

{delete_checksum_by_id,
  <<"DELETE FROM checksums where org_id = $1 AND checksum = $2">>}.

{insert_sandboxed_checksum,
 <<"INSERT INTO sandboxed_checksums(org_id, sandbox_id, checksum, created_at)
    VALUES ($1, $2, $3, $4)">>}.

{non_uploaded_checksums,
<<"SELECT sc.checksum
   FROM sandboxed_checksums AS sc
   LEFT JOIN checksums AS c
          ON sc.org_id = c.org_id
         AND sc.checksum = c.checksum
   WHERE sc.org_id = $1
     AND sc.sandbox_id = $2
     AND c.checksum IS NULL">>}.
