# Functions and macros to generate upgrade and downgrade scripts # concatenate_files(<output> <file> ...) # # Concatenate a list of files into <output> function(concatenate_files OUTPUT_FILE FIRST_FILE) file(READ ${FIRST_FILE} _contents) file(WRITE ${OUTPUT_FILE} "${_contents}") foreach(_file ${ARGN}) file(READ ${_file} _contents) file(APPEND ${OUTPUT_FILE} "${_contents}") endforeach() endfunction() # generate_script(...) # # Generate a script file and install it into the script directory. # # VERSION <version> # # Version to use for shared libraryx. # # SCRIPT <file> # # File name for script file. # # OUTPUT_DIRECTORY <dir> # # Directory for where script file should be written. Defaults to # CMAKE_CURRENT_BINARY_DIR. # # FILES <file> ... # # List of files to include, in order, in the script file. function(generate_script) set(options) set(oneValueArgs VERSION SCRIPT OUTPUT_DIRECTORY) set(multiValueArgs FILES) cmake_parse_arguments(_generate "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) if(NOT _generate_OUTPUT_DIRECTORY) set(_generate_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) endif() # Set the necessary variables, generate the real files, and append the output # to the final update script. Ideally, the template files should have a '.in' # suffix so that we can keep the template file and the final file available # for debugging, but for some reason it was decided to not use the '.in' # suffix for template files so now we're stuck with it. set(MODULE_PATHNAME "$libdir/timescaledb-${_generate_VERSION}") set(LOADER_PATHNAME "$libdir/timescaledb") # Process all files. They are template files and should end with '.in' but for # some reason they do not, so we append '.gen' instead to indicate generated # files. set(_result_files) foreach(_file ${_generate_FILES}) configure_file(${_file} ${_file}.gen @ONLY) list(APPEND _result_files ${_file}.gen) endforeach() # Concatenate the real files into the update file. message(STATUS "Generating script ${_generate_SCRIPT}") concatenate_files(${_generate_OUTPUT_DIRECTORY}/${_generate_SCRIPT} ${_result_files}) install(FILES ${_generate_OUTPUT_DIRECTORY}/${_generate_SCRIPT} DESTINATION "${PG_SHAREDIR}/extension") endfunction() # generate_downgrade_script(<options>) # # Create a downgrade script from a source version to a target version. To figure # out what files are necessary, the ScriptFiles.cmake manifest is read from the # target version. If that file does not exist in the target version, the # manifest in the current version is used, but it is assumed that files are only # added. This situation only occur in the first version where we start to # generate downgrade scripts and in this case files were only added, so we can # safely ignore them. # # SOURCE_VERSION <version> # # Version to generate downgrade script from. # # TARGET_VERSION <version> # # Version to generate downgrade script to. # # OUTPUT_DIRECTORY <dir> # # Output directory for script file. Defaults to CMAKE_CURRENT_BINARY_DIR. # # INPUT_DIRECTORY <dir> # # Input directory for downgrade files. Defaults to CMAKE_CURRENT_SOURCE_DIR. # # FILES <file> ... # # Files to include, in order, when generating the downgrade script. # # The downgrade script is generated by creating a new file of the format # "timescaledb--<current>--<version>.sql" consisting of the sequence of files: # # 1. Generated prolog from the target version # 2. Generated downgrade files from the source version # 3. Generated epilog from the target version # # The files provided are assumed to be configure templates and configure_files # will be run on them with the following variables set: # # LOADER_PATHNAME: Pathname to loader shared library. This is the same as in the # update. # # MODULE_PATHNAME: Pathname to timescale extension of the version being # dowgraded to. This can be used to load "old" functions from the correct # library. function(generate_downgrade_script) set(options) set(oneValueArgs SOURCE_VERSION TARGET_VERSION OUTPUT_DIRECTORY INPUT_DIRECTORY FILES) cmake_parse_arguments(_downgrade "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) if(NOT _downgrade_OUTPUT_DIRECTORY) set(_downgrade_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) endif() if(NOT _downgrade_INPUT_DIRECTORY) set(_downgrade_INPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) endif() foreach(_downgrade_file ${_downgrade_FILES}) if(NOT EXISTS ${_downgrade_INPUT_DIRECTORY}/${_downgrade_file}) message(FATAL_ERROR "No downgrade file ${_downgrade_file} found!") endif() endforeach() # Fetch manifest with list of files for the prolog and epilog from the target # version, if we are in a version that supports downgrades. Otherwise, take # the one in the current version. # # We have a specific exception where we allow a missing manifest for the first # version that supports downgrades and assume that the files to include are # the same in the target version as the current one. if(_downgrade_TARGET_VERSION VERSION_GREATER 2.3) git_versioned_get(VERSION ${_downgrade_TARGET_VERSION} FILES ${CMAKE_SOURCE_DIR}/cmake/ScriptFiles.cmake) else() file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/v${_downgrade_TARGET_VERSION}/cmake") file(COPY "${CMAKE_SOURCE_DIR}/cmake/ScriptFiles.cmake" DESTINATION "${CMAKE_BINARY_DIR}/v${_downgrade_TARGET_VERSION}/cmake") endif() # This will include the variables in this scope, but not in the parent scope # so we can read them locally without affecting the parent scope. include( ${CMAKE_BINARY_DIR}/v${_downgrade_TARGET_VERSION}/cmake/ScriptFiles.cmake) set(_downgrade_PRE_FILES ${PRE_DOWNGRADE_FILES}) set(_downgrade_POST_FILES "${PRE_INSTALL_FUNCTION_FILES};${SOURCE_FILES}" ${SET_POST_UPDATE_STAGE} ${POST_UPDATE_FILES} ${UNSET_UPDATE_STAGE}) # Fetch epilog from target version. git_versioned_get( VERSION ${_downgrade_TARGET_VERSION} FILES ${_downgrade_POST_FILES} RESULT_FILES _epilog_files IGNORE_ERRORS) foreach(_downgrade_file ${_downgrade_PRE_FILES}) get_filename_component(_downgrade_filename ${_downgrade_file} NAME) configure_file(${_downgrade_file} ${_downgrade_INPUT_DIRECTORY}/${_downgrade_filename} COPYONLY) list(APPEND _files ${_downgrade_INPUT_DIRECTORY}/${_downgrade_filename}) endforeach() foreach(_downgrade_file ${_downgrade_FILES}) list(APPEND _files ${_downgrade_INPUT_DIRECTORY}/${_downgrade_file}) endforeach() list(APPEND _files ${_epilog_files}) # Save the current PROJECT_VERSION_MOD set(SAVED_PROJECT_VERSION_MOD ${PROJECT_VERSION_MOD}) # To use PROJECT_VERSION_MOD variable as a target version in downgrade scripts # we should set it as the DOWNGRADE_TO_VERSION because it means the target version # when executing the downgrade scripts set(PROJECT_VERSION_MOD ${DOWNGRADE_TO_VERSION}) generate_script( VERSION ${_downgrade_TARGET_VERSION} SCRIPT timescaledb--${_downgrade_SOURCE_VERSION}--${_downgrade_TARGET_VERSION}.sql OUTPUT_DIRECTORY ${_downgrade_OUTPUT_DIRECTORY} FILES ${_files}) # Restore the original PROJECT_VERSION_MOD set(PROJECT_VERSION_MOD ${SAVED_PROJECT_VERSION_MOD}) endfunction()