module: sql-odbc-implementation Author: eec Copyright: Original Code is Copyright (c) 1995-2004 Functional Objects, Inc. All rights reserved. License: Functional Objects Library Public License Version 1.0 Dual-license: GNU Lesser General Public License Warranty: Distributed WITHOUT WARRANTY OF ANY KIND // $HopeName: D-databases-sql-odbc!binding.dylan(trunk.9) $ define sideways method default-conversion(value :: ) => (converted-value :: ) make(, year: value.year-value, month: value.month-value, day: value.day-value, hours: value.hour-value, minutes: value.minute-value, seconds: value.second-value, microseconds: 0) end method; define open generic sql-binding-info(object :: ) // should be sealed but due to a bug in the compiler... => (sql-data-type :: , precision :: , scale :: ); define method sql-binding-info(object == $null-value) => (sql-data-type :: , precision :: , scale :: ) sql-binding-info(0) // Since the value is null, we don't care // what we are binding to. end method; define method sql-binding-info(object :: ) => (sql-data-type :: , precision :: , scale :: ) values($sql-integer, 0, 0) end method; define method sql-binding-info(object :: big/) => (sql-data-type :: , precision :: , scale :: ) values($sql-integer, 0, 0) end method; define method sql-binding-info(object :: ) => (sql-data-type :: , precision :: , scale :: ) values($sql-char, 1, 0) end method; define method sql-binding-info(object :: ) => (sql-data-type :: , precision :: , scale :: ) values($sql-double, 5, 5); //+++ Correct precision and scale should be used! end method; define method sql-binding-info(object :: ) => (sql-data-type :: , precision :: , scale :: ) //+ The let statement is needed to squelch a bogus type inference warning let string-size :: = object.size; values($sql-varchar, string-size, 0) end method; define method sql-binding-info(object :: ) => (sql-data-type :: , precision :: , scale :: ) values($sql-type-timestamp, 19, 0) end method; define generic create-storage(sql-data-type :: , precision :: , scale :: , element-count :: , #key initial-value :: ) => (c-data-type :: , storage :: , storage-size :: , data-size :: ); define not-inline method create-storage(sql-data-type :: , precision :: , scale :: , element-count :: , #key initial-value :: ) => (c-data-type :: , storage :: , storage-size :: , data-size :: ); signal(make(, format-string: "Binding to column whose datatype (%=) is not supported\n" "Using instance of instead.\n", format-arguments: sql-data-type)); values($sql-unsupported-type, null-pointer(), 0, 0); end method; define not-inline method create-storage(sql-data-type == $sql-unknown-type, precision :: , scale :: , element-count :: , #key initial-value :: ) => (c-data-type :: , storage :: , storage-size :: , data-size :: ); signal(make(, format-string: "Attempting to bind to a column with a datatype that " "ODBC does not recognize.\n" "Using instance of instead.\n")); values($sql-unknown-type, null-pointer(), 0, 0); end method; define not-inline method create-storage(sql-data-type == $sql-char, precision :: , scale :: , element-count :: , #key initial-value :: false-or()) => (c-data-type :: , storage :: , storage-size :: , data-size :: ) let instance-size = precision; let storage-size = instance-size * element-count; let storage = make(, element-count: storage-size); if (initial-value ~= #f) let target-value = as(, initial-value); // ffi treats chars as ints--sigh. for (ndx :: from 0 below storage-size) storage[ndx] := target-value; end for; end if; values($sql-c-char, storage, storage-size, instance-size); end method; define not-inline method create-storage(sql-data-type == $sql-type-date, precision :: , scale :: , element-count :: , #key initial-value :: false-or()) => (c-data-type :: , storage :: , storage-size :: , data-size :: ) let instance-size = size-of(); let storage-size = instance-size * element-count; let storage = make(, element-count: element-count); if (initial-value ~= #f) for (ndx :: from 0 below element-count) storage[ndx].year-value := initial-value.date-year; storage[ndx].month-value := initial-value.date-month; storage[ndx].day-value := initial-value.date-day; end for; end if; values($sql-c-type-date, storage, storage-size, instance-size); end method; define not-inline method create-storage(sql-data-type == $sql-type-time, precision :: , scale :: , element-count :: , #key initial-value :: false-or()) => (c-data-type :: , storage :: , storage-size :: , data-size :: ) let instance-size = size-of(); let storage-size = instance-size * element-count; let storage = make(, element-count: element-count); if (initial-value ~= #f) for (ndx :: from 0 below element-count) storage[ndx].hour-value := initial-value.date-hours; storage[ndx].minute-value := initial-value.date-minutes; storage[ndx].second-value := initial-value.date-seconds; storage[ndx].fraction-value := 0; end for; end if; values($sql-c-type-time, storage, storage-size, instance-size); end method; define not-inline method create-storage(sql-data-type == $sql-type-timestamp, precision :: , scale :: , element-count :: , #key initial-value :: false-or()) => (c-data-type :: , storage :: , storage-size :: , data-size :: ) let instance-size = size-of(); let storage-size = instance-size * element-count; let storage = make(, element-count: element-count); if (initial-value ~= #f) for (ndx :: from 0 below element-count) storage[ndx].year-value := initial-value.date-year; storage[ndx].month-value := initial-value.date-month; storage[ndx].day-value := initial-value.date-day; storage[ndx].hour-value := initial-value.date-hours; storage[ndx].minute-value := initial-value.date-minutes; storage[ndx].second-value := initial-value.date-seconds; storage[ndx].fraction-value := 0; end for; end if; values($sql-c-type-timestamp, storage, storage-size, instance-size); end method; define not-inline method create-storage(sql-data-type == $sql-datetime, precision :: , scale :: , element-count :: , #key initial-value :: false-or()) => (c-data-type :: , storage :: , storage-size :: , data-size :: ) let instance-size = size-of(); let storage-size = instance-size * element-count; let storage = make(, element-count: element-count); if (initial-value ~= #f) for (ndx :: from 0 below element-count) storage[ndx].year-value := initial-value.date-year; storage[ndx].month-value := initial-value.date-month; storage[ndx].day-value := initial-value.date-day; storage[ndx].hour-value := initial-value.date-hours; storage[ndx].minute-value := initial-value.date-minutes; storage[ndx].second-value := initial-value.date-seconds; storage[ndx].fraction-value := 0; end for; end if; values($sql-c-type-timestamp, storage, storage-size, instance-size); end method; define not-inline method create-storage(sql-data-type == $sql-numeric, precision :: , scale :: , element-count :: , #key initial-value :: false-or()) => (c-data-type :: , storage :: , storage-size :: , data-size :: ) let instance-size = precision + 1; let storage-size = instance-size * element-count; let storage = make(, size: storage-size); if (initial-value ~= #f) let target-value = as(, initial-value); for (element-index :: from 0 below element-count) for (char-index :: from 0 below instance-size - 1) storage[element-index * char-index] := target-value[char-index]; finally storage[element-index * char-index + 1] := null-pointer(); end for; end for; end if; values($sql-c-char, storage, storage-size, instance-size); end method; define not-inline method create-storage(sql-data-type == $sql-decimal, precision :: , scale :: , element-count :: , #key initial-value :: false-or()) => (c-data-type :: , storage :: , storage-size :: , data-size :: ) let instance-size = precision; let storage-size = instance-size * element-count; let storage = make(, element-count: storage-size); if (initial-value ~= #f) let target-value = as(, initial-value); for (element-index :: from 0 below element-count) for (char-index :: from 0 below instance-size - 1) storage[element-index * char-index] := target-value[char-index]; finally storage[element-index * char-index + 1] := null-pointer(); end for; end for; end if; values($sql-c-char, storage, storage-size, instance-size); end method; /* Because you can't specialize a method on a keyword argument, we have three helper methods to actually create the appropriate storage. Big-integers need the c-raw versions of the storage in order to stash a machine-word into them; the non-raw (cooked?) versions of c storage automatically convert integers, and can't handle big-integers. */ define method create-storage-helper(value :: , element-count :: ) => result :: ; let storage = make(, element-count: element-count); storage; end; define method create-storage-helper(value :: , element-count :: ) => result :: ; let storage = make(, element-count: element-count); for (ndx :: from 0 below element-count) storage[ndx] := value; end for; storage; end; define method create-storage-helper(value :: big/, element-count :: ) => result :: ; let storage = make(, element-count: element-count); for (ndx :: from 0 below element-count) storage[ndx] := as(, value); end for; end; define not-inline method create-storage(sql-data-type == $sql-integer, precision :: , scale :: , element-count :: , #key initial-value :: false-or(big/)) => (c-data-type :: , storage :: , storage-size :: , data-size :: ) let instance-size = size-of(); let storage-size = instance-size * element-count; let storage = create-storage-helper(initial-value, element-count); clear-memory!(storage, storage-size); values($sql-c-long, storage, storage-size, instance-size); end method; define not-inline method create-storage(sql-data-type == $sql-smallint, precision :: , scale :: , element-count :: , #key initial-value :: false-or()) => (c-data-type :: , storage :: , storage-size :: , data-size :: ) let instance-size = size-of(); let storage-size = instance-size * element-count; let storage = make(, element-count: element-count); if (initial-value ~= #f) for (ndx :: from 0 below element-count) storage[ndx] := initial-value; end for; end if; values($sql-c-short, storage, storage-size, instance-size); end method; define not-inline method create-storage(sql-data-type == $sql-tinyint, precision :: , scale :: , element-count :: , #key initial-value :: false-or()) => (c-data-type :: , storage :: , storage-size :: , data-size :: ) let instance-size = size-of(); let storage-size = instance-size * element-count; let storage = make(, element-count: element-count); if (initial-value ~= #f) for (ndx :: from 0 below element-count) storage[ndx] := initial-value; end for; end if; values($sql-c-short, storage, storage-size, instance-size); end method; define not-inline method create-storage(sql-data-type == $sql-float, precision :: , scale :: , element-count :: , #key initial-value :: false-or()) => (c-data-type :: , storage :: , storage-size :: , data-size :: ) let instance-size = size-of(); let storage-size = instance-size * element-count; let storage = make(, element-count: element-count); if (initial-value ~= #f) for (ndx :: from 0 below element-count) storage[ndx] := initial-value; end for; end if; values($sql-c-double, storage, storage-size, instance-size); end method; define not-inline method create-storage(sql-data-type == $sql-real, precision :: , scale :: , element-count :: , #key initial-value :: false-or()) => (c-data-type :: , storage :: , storage-size :: , data-size :: ) create-storage($sql-double, precision, scale, element-count, initial-value: initial-value); end method; define not-inline method create-storage(sql-data-type == $sql-double, precision :: , scale :: , element-count :: , #key initial-value :: false-or()) => (c-data-type :: , storage :: , storage-size :: , data-size :: ) let instance-size = size-of(); let storage-size = instance-size * element-count; let storage = make(, element-count: element-count); if (initial-value ~= #f) for (ndx :: from 0 below element-count) storage[ndx] := as(, initial-value); end for; end if; values($sql-c-double, storage, storage-size, instance-size); end method; define not-inline method create-storage(sql-data-type == $sql-varchar, precision :: , scale :: , element-count :: , #key initial-value :: false-or()) => (c-data-type :: , storage :: , storage-size :: , data-size :: ) let instance-size = precision + 1; let storage-size = instance-size * element-count; let storage = make(, element-count: storage-size); if (initial-value ~= #f) let target-value = as(, initial-value); for (element-index :: from 0 below element-count) for (char-index :: from 0 below instance-size) storage[char-index * element-index] := target-value[char-index]; finally storage[char-index * element-index + 1] := null-pointer(); end for; end for; end if; values($sql-c-char, storage, storage-size, instance-size); end method; define constant $none = #"none"; define constant = type-union(, $none); define sealed concrete class () //constant slot name :: , // init-keyword: name:, // init-value: ""; constant slot sql-data-type :: , required-init-keyword: sql-data-type:; slot c-data-type :: , init-value: $sql-c-char; constant slot precision :: , required-init-keyword: precision:; constant slot scale :: , required-init-keyword: scale:; // This slot isn't used anywhere. Should it be? //constant slot nullable :: , // init-keyword: nullable:, // init-value: #f; slot storage :: ; slot storage-size :: ; slot data-length :: = .null-pointer; required keyword rowset-size; end class; define method initialize(binding :: , #key initial-value, rowset-size :: ) next-method(); let (the-data-type, a-storage, a-storage-size, a-data-size) = create-storage(binding.sql-data-type, binding.precision, binding.scale, rowset-size, initial-value: if (initial-value == $null-value) #f else initial-value end if); binding.c-data-type := the-data-type; binding.storage := a-storage; binding.storage-size := a-storage-size; binding.data-length := make(, element-count: rowset-size); let data-length-value = if (initial-value == $null-value) $sql-null-data else a-data-size end if; for (ndx :: from 0 below rowset-size) binding.data-length[ndx] := data-length-value; end for; finalize-when-unreachable(binding); end method; define method finalize(binding :: ) => () if (null-pointer?(binding.storage)) destroy(binding.storage); binding.storage := null-pointer(); end if; if (null-pointer?(binding.data-length)) destroy(binding.data-length); binding.data-length := null-pointer(); end if; notify-of-finalization(binding); next-method(); end method;