* Starts a new query cursor file. This is called * on the first page of a query. * Registers the file in the cursor directory and * returns an opaque structure to track this cursor. * TODO: Need to apply storage backpressure for the cursor * files. */
| 257 | * files. |
| 258 | */ |
| 259 | CursorFileState * |
| 260 | CreateCursorFile(const char *cursorName) |
| 261 | { |
| 262 | if (!cursor_set_initialized) |
| 263 | { |
| 264 | ereport(ERROR, (errmsg( |
| 265 | "Cursor storage has not been properly initialized. Before using cursors, the server must be restarted"))); |
| 266 | } |
| 267 | |
| 268 | if (!UseFileBasedPersistedCursors) |
| 269 | { |
| 270 | ereport(ERROR, (errmsg("File based cursors are not enabled. " |
| 271 | "set %s.useFileBasedPersistedCursors to true", |
| 272 | ApiGucPrefix))); |
| 273 | } |
| 274 | |
| 275 | if (strlen(cursorName) + strlen(cursor_directory) >= (NAMEDATALEN - 5)) |
| 276 | { |
| 277 | ereport(ERROR, (errmsg( |
| 278 | "Cursor name exceeds the max allowed length."))); |
| 279 | } |
| 280 | |
| 281 | CursorFileState *fileState = palloc0(sizeof(CursorFileState)); |
| 282 | snprintf(fileState->cursorState.cursorFileName, NAMEDATALEN, "%s/%s", |
| 283 | cursor_directory, cursorName); |
| 284 | |
| 285 | File cursorFile = PathNameOpenTemporaryFile(fileState->cursorState.cursorFileName, |
| 286 | O_RDWR | O_CREAT | O_EXCL | PG_BINARY); |
| 287 | if (cursorFile < 0) |
| 288 | { |
| 289 | if (errno == EEXIST) |
| 290 | { |
| 291 | ereport(ERROR, (errcode(ERRCODE_DOCUMENTDB_CURSORINUSE), |
| 292 | errmsg("Cursor already present on server: %s", cursorName))); |
| 293 | } |
| 294 | else |
| 295 | { |
| 296 | ereport(ERROR, |
| 297 | (errcode(ERRCODE_DOCUMENTDB_INTERNALERROR), |
| 298 | errmsg("Failed to open file \"%s\": %m", |
| 299 | fileState->cursorState.cursorFileName))); |
| 300 | } |
| 301 | } |
| 302 | |
| 303 | if (FileSize(cursorFile) != 0) |
| 304 | { |
| 305 | ereport(ERROR, (errcode(ERRCODE_DOCUMENTDB_CURSORINUSE), |
| 306 | errmsg("Cursor already present on server: %s", cursorName))); |
| 307 | } |
| 308 | |
| 309 | /* Register the cursor file for transaction abort */ |
| 310 | strncpy(PendingCursorFile, fileState->cursorState.cursorFileName, NAMEDATALEN); |
| 311 | |
| 312 | /* Ensure we have sufficient space to create cursor files */ |
| 313 | if (!IncrementCursorCount()) |
| 314 | { |
| 315 | /* We've reached capacity, try to clean up and try again */ |
| 316 | TryCleanUpAndReserveCursor(); |
no test coverage detected